From 171f6f08943dd1d87120df3e4894ddcfd5e1d246 Mon Sep 17 00:00:00 2001
From: Alexander Tumin <iamtakingiteasy@eientei.org>
Date: Sat, 6 Aug 2022 17:26:43 +0300
Subject: Lists implementation

---
 src/components/nav_panel/nav_panel.vue | 45 ++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 3fd27d89..9322e233 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -25,6 +25,51 @@
             <TimelineMenuContent class="timelines" />
           </div>
         </li>
+        <li v-if="currentUser && listsNavigation">
+          <button
+            class="button-unstyled menu-item"
+            @click="toggleLists"
+          >
+            <router-link
+              :to="{ name: 'lists' }"
+              @click.stop
+            >
+              <FAIcon
+                fixed-width
+                class="fa-scale-110"
+                icon="list"
+              />{{ $t("nav.lists") }}
+            </router-link>
+            <FAIcon
+              class="timelines-chevron"
+              fixed-width
+              :icon="showLists ? 'chevron-up' : 'chevron-down'"
+            />
+          </button>
+          <div
+            v-show="showLists"
+            class="timelines-background"
+          >
+            <ListsMenuContent class="timelines" />
+          </div>
+        </li>
+        <li v-if="currentUser && !listsNavigation">
+          <router-link
+            :to="{ name: 'lists' }"
+            @click.stop
+          >
+            <button
+              class="button-unstyled menu-item"
+              @click="toggleLists"
+            >
+              <FAIcon
+                fixed-width
+                class="fa-scale-110"
+                icon="list"
+              />{{ $t("nav.lists") }}
+            </button>
+          </router-link>
+        </li>
         <li v-if="currentUser">
           <router-link
             class="menu-item"
-- 
cgit v1.2.3-70-g09d2


From 6df99133548fb209bf365b77665931be477f0a30 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Thu, 11 Aug 2022 14:30:58 +0300
Subject: ability to pin items in navigation menu, initial draft version

---
 src/App.scss                                       |   3 +
 src/components/nav_panel/nav_panel.js              | 111 +++++++++++++++-
 src/components/nav_panel/nav_panel.vue             | 140 ++++++++++-----------
 src/components/settings_modal/tabs/general_tab.vue |   5 -
 src/components/timeline_menu/timeline_menu.vue     |   2 +-
 .../timeline_menu/timeline_menu_content.js         |  27 +++-
 .../timeline_menu/timeline_menu_content.vue        |  69 +++-------
 src/modules/config.js                              |   1 -
 src/modules/serverSideStorage.js                   |   9 +-
 9 files changed, 221 insertions(+), 146 deletions(-)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/App.scss b/src/App.scss
index ab025d63..c75c990a 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -756,6 +756,9 @@ option {
     padding: 0 0.3em;
   }
 }
+.veryfaint {
+  opacity: 0.25;
+}
 
 .login-hint {
   text-align: center;
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index abeff6bf..758f9af4 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -31,6 +31,66 @@ library.add(
   faList
 )
 
+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'
+  },
+  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'
+  }
+}
+
 const NavPanel = {
   created () {
     if (this.currentUser && this.currentUser.locked) {
@@ -43,8 +103,11 @@ const NavPanel = {
   },
   data () {
     return {
+      collapsed: false,
       showTimelines: false,
-      showLists: false
+      showLists: false,
+      timelinesList: Object.entries(TIMELINES).map(([k, v]) => ({ ...v, name: k })),
+      rootList: Object.entries(ROOT_ITEMS).map(([k, v]) => ({ ...v, name: k }))
     }
   },
   methods: {
@@ -53,19 +116,57 @@ const NavPanel = {
     },
     toggleLists () {
       this.showLists = !this.showLists
+    },
+    toggleCollapse () {
+      this.collapsed = !this.collapsed
+    },
+    isPinned (item) {
+      return this.pinnedItems.has(item)
+    },
+    togglePin (item) {
+      if (this.isPinned(item)) {
+        this.$store.commit('removeCollectionPreference', { path: 'collections.pinnedNavItems', value: item })
+      } else {
+        this.$store.commit('addCollectionPreference', { path: 'collections.pinnedNavItems', value: item })
+      }
     }
   },
   computed: {
-    listsNavigation () {
-      return this.$store.getters.mergedConfig.listsNavigation
-    },
     ...mapState({
       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
+      pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
+      pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems)
     }),
+    rootItems () {
+      return Object
+        .entries({ ...ROOT_ITEMS })
+        .map(([k, v]) => ({ ...v, name: k }))
+        .filter(({ criteria, anon, anonRoute }) => {
+          const set = new Set(criteria || [])
+          if (!this.federating && set.has('federating')) return false
+          if (this.private && set.has('!private')) return false
+          if (!this.currentUser && !(anon || anonRoute)) return false
+          if ((!this.currentUser || !this.currentUser.locked) && set.has('lockedUser')) return false
+          return true
+        })
+    },
+    pinnedList () {
+      return Object
+        .entries({ ...TIMELINES, ...ROOT_ITEMS })
+        .filter(([k]) => this.pinnedItems.has(k))
+        .map(([k, v]) => ({ ...v, name: k }))
+        .filter(({ criteria, anon, anonRoute }) => {
+          const set = new Set(criteria || [])
+          if (!this.federating && set.has('federating')) return false
+          if (this.private && set.has('!private')) return false
+          if (!this.currentUser && !(anon || anonRoute)) return false
+          if (this.currentUser && !this.currentUser.locked && set.has('locked')) return false
+          return true
+        })
+    },
     ...mapGetters(['unreadChatCount'])
   }
 }
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 9322e233..99a4571e 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -1,7 +1,33 @@
 <template>
   <div class="NavPanel">
     <div class="panel panel-default">
-      <ul>
+      <div class="panel-heading">
+        <span>
+          <span v-for="item in pinnedList" :key="item.name" class="pinned-item">
+            <router-link
+              :to="{ name: (currentUser || item.anon) ? item.route : item.anonRoute, params: { username: currentUser.screen_name } }"
+            >
+              <FAIcon
+                fixed-width
+                class="fa-scale-110 fa-old-padding "
+                :icon="item.icon"
+              />
+            </router-link>
+          </span>
+        </span>
+        <div class="spacer"/>
+        <button
+          class="button-unstyled"
+          @click="toggleCollapse"
+        >
+          <FAIcon
+            class="timelines-chevron"
+            fixed-width
+            :icon="collapsed ? 'chevron-down' : 'chevron-up'"
+          />
+        </button>
+      </div>
+      <ul class="panel-body" v-if="!collapsed">
         <li v-if="currentUser || !privateMode">
           <button
             class="button-unstyled menu-item"
@@ -22,29 +48,34 @@
             v-show="showTimelines"
             class="timelines-background"
           >
-            <TimelineMenuContent class="timelines" />
+            <TimelineMenuContent class="timelines" :content="timelinesList" />
           </div>
         </li>
-        <li v-if="currentUser && listsNavigation">
+        <li v-if="currentUser">
           <button
             class="button-unstyled menu-item"
             @click="toggleLists"
           >
-            <router-link
-              :to="{ name: 'lists' }"
-              @click.stop
-            >
               <FAIcon
                 fixed-width
                 class="fa-scale-110"
                 icon="list"
               />{{ $t("nav.lists") }}
-            </router-link>
             <FAIcon
               class="timelines-chevron"
               fixed-width
               :icon="showLists ? 'chevron-up' : 'chevron-down'"
             />
+            <router-link
+              :to="{ name: 'lists' }"
+              @click.stop
+            >
+              <FAIcon
+                class="timelines-chevron"
+                fixed-width
+                icon="wrench"
+              />
+            </router-link>
           </button>
           <div
             v-show="showLists"
@@ -53,83 +84,31 @@
             <ListsMenuContent class="timelines" />
           </div>
         </li>
-        <li v-if="currentUser && !listsNavigation">
+        <li v-for="item in rootItems" :key="item.name">
           <router-link
-            :to="{ name: 'lists' }"
-            @click.stop
+            class="menu-item"
+            :to="{ name: (currentUser || item.anon) ? item.route : item.anonRoute, params: { username: currentUser.screen_name } }"
           >
+            <FAIcon
+              fixed-width
+              class="fa-scale-110 fa-old-padding "
+              :icon="item.icon"
+            />{{ $t(item.label) }}
             <button
-              class="button-unstyled menu-item"
-              @click="toggleLists"
-            >
+              type="button"
+              class="button-unstyled"
+              @click.stop.prevent="togglePin(item.name)"
+              >
               <FAIcon
                 fixed-width
-                class="fa-scale-110"
-                icon="list"
-              />{{ $t("nav.lists") }}
+                class="fa-scale-110 fa-old-padding "
+                :class="{ 'veryfaint': !isPinned(item.name) }"
+                :transform="!isPinned(item.name) ? 'rotate-45' : ''"
+                icon="thumbtack"
+              />
             </button>
           </router-link>
         </li>
-        <li v-if="currentUser">
-          <router-link
-            class="menu-item"
-            :to="{ name: 'interactions', params: { username: currentUser.screen_name } }"
-          >
-            <FAIcon
-              fixed-width
-              class="fa-scale-110"
-              icon="bell"
-            />{{ $t("nav.interactions") }}
-          </router-link>
-        </li>
-        <li v-if="currentUser && pleromaChatMessagesAvailable">
-          <router-link
-            class="menu-item"
-            :to="{ name: 'chats', params: { username: currentUser.screen_name } }"
-          >
-            <div
-              v-if="unreadChatCount"
-              class="badge badge-notification"
-            >
-              {{ unreadChatCount }}
-            </div>
-            <FAIcon
-              fixed-width
-              class="fa-scale-110"
-              icon="comments"
-            />{{ $t("nav.chats") }}
-          </router-link>
-        </li>
-        <li v-if="currentUser && currentUser.locked">
-          <router-link
-            class="menu-item"
-            :to="{ name: 'friend-requests' }"
-          >
-            <FAIcon
-              fixed-width
-              class="fa-scale-110"
-              icon="user-plus"
-            />{{ $t("nav.friend_requests") }}
-            <span
-              v-if="followRequestCount > 0"
-              class="badge badge-notification"
-            >
-              {{ followRequestCount }}
-            </span>
-          </router-link>
-        </li>
-        <li>
-          <router-link
-            class="menu-item"
-            :to="{ name: 'about' }"
-          >
-            <FAIcon
-              fixed-width
-              class="fa-scale-110"
-              icon="info-circle"
-            />{{ $t("nav.about") }}
-          </router-link>
-        </li>
       </ul>
     </div>
   </div>
@@ -246,5 +225,12 @@
     right: 0.6rem;
     top: 1.25em;
   }
+
+  .pinned-item {
+    .router-link-exact-active .svg-inline--fa {
+      color: $fallback--text;
+      color: var(--selectedMenuText, $fallback--text);
+    }
+  }
 }
 </style>
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 91015955..ae97a744 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -124,11 +124,6 @@
             {{ $t('settings.hide_shoutbox') }}
           </BooleanSetting>
         </li>
-        <li>
-          <BooleanSetting path="listsNavigation">
-            {{ $t('settings.lists_navigation') }}
-          </BooleanSetting>
-        </li>
         <li>
           <h3>{{ $t('settings.columns') }}</h3>
         </li>
diff --git a/src/components/timeline_menu/timeline_menu.vue b/src/components/timeline_menu/timeline_menu.vue
index c24b9d72..5ef96328 100644
--- a/src/components/timeline_menu/timeline_menu.vue
+++ b/src/components/timeline_menu/timeline_menu.vue
@@ -10,7 +10,7 @@
     @close="() => isOpen = false"
   >
     <template #content>
-      <TimelineMenuContent />
+      <TimelineMenuContent :content="timelinesList" />
     </template>
     <template #trigger>
       <span class="button-unstyled title timeline-menu-title">
diff --git a/src/components/timeline_menu/timeline_menu_content.js b/src/components/timeline_menu/timeline_menu_content.js
index 671570dd..c5b1cff2 100644
--- a/src/components/timeline_menu/timeline_menu_content.js
+++ b/src/components/timeline_menu/timeline_menu_content.js
@@ -17,12 +17,35 @@ library.add(
 )
 
 const TimelineMenuContent = {
+  props: ['content'],
+  methods: {
+    isPinned (item) {
+      return this.pinnedItems.has(item)
+    },
+    togglePin (item) {
+      if (this.isPinned(item)) {
+        this.$store.commit('removeCollectionPreference', { path: 'collections.pinnedNavItems', value: item })
+      } else {
+        this.$store.commit('addCollectionPreference', { path: 'collections.pinnedNavItems', value: item })
+      }
+    }
+  },
   computed: {
     ...mapState({
       currentUser: state => state.users.currentUser,
       privateMode: state => state.instance.private,
-      federating: state => state.instance.federating
-    })
+      federating: state => state.instance.federating,
+      pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems)
+    }),
+    list () {
+      return (this.content || []).filter(({ criteria, anon, anonRoute }) => {
+        const set = new Set(criteria || [])
+        if (!this.federating && set.has('federating')) return false
+        if (this.private && set.has('!private')) return false
+        if (!this.currentUser && !(anon || anonRoute)) return false
+        return true
+      })
+    }
   }
 }
 
diff --git a/src/components/timeline_menu/timeline_menu_content.vue b/src/components/timeline_menu/timeline_menu_content.vue
index 59e9e43c..7dc76b62 100644
--- a/src/components/timeline_menu/timeline_menu_content.vue
+++ b/src/components/timeline_menu/timeline_menu_content.vue
@@ -1,63 +1,28 @@
 <template>
   <ul>
-    <li v-if="currentUser">
+    <li v-for="item in list" :key="item.name">
       <router-link
         class="menu-item"
-        :to="{ name: 'friends' }"
+        :to="{ name: (currentUser || item.anon) ? item.route : item.anonRoute, params: { username: currentUser.screen_name } }"
       >
         <FAIcon
           fixed-width
           class="fa-scale-110 fa-old-padding "
-          icon="home"
-        />{{ $t("nav.home_timeline") }}
-      </router-link>
-    </li>
-    <li v-if="currentUser || !privateMode">
-      <router-link
-        class="menu-item"
-        :to="{ name: 'public-timeline' }"
-      >
-        <FAIcon
-          fixed-width
-          class="fa-scale-110 fa-old-padding "
-          icon="users"
-        />{{ $t("nav.public_tl") }}
-      </router-link>
-    </li>
-    <li v-if="federating && (currentUser || !privateMode)">
-      <router-link
-        class="menu-item"
-        :to="{ name: 'public-external-timeline' }"
-      >
-        <FAIcon
-          fixed-width
-          class="fa-scale-110 fa-old-padding "
-          icon="globe"
-        />{{ $t("nav.twkn") }}
-      </router-link>
-    </li>
-    <li v-if="currentUser">
-      <router-link
-        class="menu-item"
-        :to="{ name: 'bookmarks'}"
-      >
-        <FAIcon
-          fixed-width
-          class="fa-scale-110 fa-old-padding "
-          icon="bookmark"
-        />{{ $t("nav.bookmarks") }}
-      </router-link>
-    </li>
-    <li v-if="currentUser">
-      <router-link
-        class="menu-item"
-        :to="{ name: 'dms', params: { username: currentUser.screen_name } }"
-      >
-        <FAIcon
-          fixed-width
-          class="fa-scale-110 fa-old-padding "
-          icon="envelope"
-        />{{ $t("nav.dms") }}
+          :icon="item.icon"
+        />{{ $t(item.label) }}
+        <button
+          type="button"
+          class="button-unstyled"
+          @click.stop.prevent="togglePin(item.name)"
+          >
+          <FAIcon
+            fixed-width
+            class="fa-scale-110 fa-old-padding "
+            :class="{ 'veryfaint': !isPinned(item.name) }"
+            :transform="!isPinned(item.name) ? 'rotate-45' : ''"
+            icon="thumbtack"
+          />
+        </button>
       </router-link>
     </li>
   </ul>
diff --git a/src/modules/config.js b/src/modules/config.js
index c34b2c8c..f52ec105 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -87,7 +87,6 @@ export const defaultState = {
   sidebarColumnWidth: '25rem',
   contentColumnWidth: '45rem',
   notifsColumnWidth: '25rem',
-  listsNavigation: false,
   greentext: undefined, // instance default
   useAtIcon: undefined, // instance default
   mentionLinkDisplay: undefined, // instance default
diff --git a/src/modules/serverSideStorage.js b/src/modules/serverSideStorage.js
index bff8e4ba..c4c5ebba 100644
--- a/src/modules/serverSideStorage.js
+++ b/src/modules/serverSideStorage.js
@@ -23,6 +23,9 @@ export const defaultState = {
     _journal: [],
     simple: {
       dontShowUpdateNotifs: false
+    },
+    collections: {
+      pinnedNavItems: ['home', 'dms', 'chats', 'about']
     }
   },
   // raw data
@@ -274,8 +277,8 @@ export const mutations = {
 
     totalFlags = _resetFlags(totalFlags)
 
-    recent.flagStorage = totalFlags
-    recent.prefsStorage = totalPrefs
+    recent.flagStorage = { ...flagsTemplate, ...totalFlags }
+    recent.prefsStorage = { ...defaultState.prefsStorage, ...totalPrefs }
 
     state.dirty = dirty || needsUpload
     state.cache = recent
@@ -320,7 +323,7 @@ export const mutations = {
       return
     }
     const collection = new Set(get(state.prefsStorage, path))
-    collection.remove(value)
+    collection.delete(value)
     set(state.prefsStorage, path, collection)
     state.prefsStorage._journal = [
       ...state.prefsStorage._journal,
-- 
cgit v1.2.3-70-g09d2


From 77127e2a588abb5bf329506ff7e006021b086e90 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Thu, 11 Aug 2022 21:00:27 +0300
Subject: you can now pin lists

---
 src/App.scss                                       |  15 ++-
 src/components/lists_menu/lists_menu_content.js    |  32 +++---
 src/components/lists_menu/lists_menu_content.vue   |  16 +--
 src/components/nav_panel/nav_panel.js              | 123 ++++++---------------
 src/components/nav_panel/nav_panel.vue             |  56 +++-------
 src/components/timeline/timeline.js                |   1 +
 src/components/timeline/timeline.vue               |   2 +-
 src/components/timeline_menu/timeline_menu.js      |  16 ++-
 src/components/timeline_menu/timeline_menu.vue     |   8 +-
 .../timeline_menu/timeline_menu_content.js         |  52 ---------
 .../timeline_menu/timeline_menu_content.vue        |  31 ------
 11 files changed, 103 insertions(+), 249 deletions(-)
 delete mode 100644 src/components/timeline_menu/timeline_menu_content.js
 delete mode 100644 src/components/timeline_menu/timeline_menu_content.vue

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/App.scss b/src/App.scss
index c75c990a..4c9a8884 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -117,8 +117,15 @@ h4 {
   margin: 0;
 }
 
+.iconLetter {
+  display: inline-block;
+  text-align: center;
+  font-weight: 1000;
+}
+
 i[class*=icon-],
-.svg-inline--fa {
+.svg-inline--fa,
+.iconLetter {
   color: $fallback--icon;
   color: var(--icon, $fallback--icon);
 }
@@ -746,13 +753,15 @@ option {
 }
 
 .fa-scale-110 {
-  &.svg-inline--fa {
+  &.svg-inline--fa,
+  &.iconLetter {
     font-size: 1.1em;
   }
 }
 
 .fa-old-padding {
-  &.svg-inline--fa {
+  &.svg-inline--fa,
+  &.iconLetter {
     padding: 0 0.3em;
   }
 }
diff --git a/src/components/lists_menu/lists_menu_content.js b/src/components/lists_menu/lists_menu_content.js
index 37e7868c..99fea0f0 100644
--- a/src/components/lists_menu/lists_menu_content.js
+++ b/src/components/lists_menu/lists_menu_content.js
@@ -1,28 +1,26 @@
 import { mapState } from 'vuex'
-import { library } from '@fortawesome/fontawesome-svg-core'
-import {
-  faUsers,
-  faGlobe,
-  faBookmark,
-  faEnvelope,
-  faHome
-} from '@fortawesome/free-solid-svg-icons'
+import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
 
-library.add(
-  faUsers,
-  faGlobe,
-  faBookmark,
-  faEnvelope,
-  faHome
-)
+export const getListEntries = state => state.lists.allLists.map(list => ({
+  name: 'list-' + list.id,
+  routeObject: { name: 'lists-timeline', params: { id: list.id } },
+  labelRaw: list.title,
+  iconLetter: list.title[0]
+}))
 
-const ListsMenuContent = {
+export const ListsMenuContent = {
+  props: [
+    'showPin'
+  ],
   created () {
     this.$store.dispatch('startFetchingLists')
   },
+  components: {
+    NavigationEntry
+  },
   computed: {
     ...mapState({
-      lists: state => state.lists.allLists,
+      lists: getListEntries,
       currentUser: state => state.users.currentUser,
       privateMode: state => state.instance.private,
       federating: state => state.instance.federating
diff --git a/src/components/lists_menu/lists_menu_content.vue b/src/components/lists_menu/lists_menu_content.vue
index e910d6eb..9c58b092 100644
--- a/src/components/lists_menu/lists_menu_content.vue
+++ b/src/components/lists_menu/lists_menu_content.vue
@@ -1,17 +1,7 @@
 <template>
-  <ul>
-    <li
-      v-for="list in lists.slice().reverse()"
-      :key="list.id"
-    >
-      <router-link
-        class="menu-item"
-        :to="{ name: 'lists-timeline', params: { id: list.id } }"
-      >
-        {{ list.title }}
-      </router-link>
-    </li>
-  </ul>
+<ul>
+  <NavigationEntry v-for="item in lists" :key="item.name" :show-pin="showPin" :item="item" />
+</ul>
 </template>
 
 <script src="./lists_menu_content.js"></script>
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index 7cc3122d..10998113 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -1,6 +1,8 @@
-import TimelineMenuContent from '../timeline_menu/timeline_menu_content.vue'
-import ListsMenuContent from '../lists_menu/lists_menu_content.vue'
+import { getListEntries, ListsMenuContent } from '../lists_menu/lists_menu_content.vue'
 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 { library } from '@fortawesome/fontawesome-svg-core'
 import {
@@ -30,67 +32,6 @@ library.add(
   faStream,
   faList
 )
-
-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'
-  },
-  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'
-  }
-}
-
 const NavPanel = {
   created () {
     if (this.currentUser && this.currentUser.locked) {
@@ -98,8 +39,8 @@ const NavPanel = {
     }
   },
   components: {
-    TimelineMenuContent,
-    ListsMenuContent
+    ListsMenuContent,
+    NavigationEntry
   },
   data () {
     return {
@@ -134,6 +75,7 @@ const NavPanel = {
   },
   computed: {
     ...mapState({
+      lists: getListEntries,
       currentUser: state => state.users.currentUser,
       followRequestCount: state => state.api.followRequests.length,
       privateMode: state => state.instance.private,
@@ -143,31 +85,36 @@ const NavPanel = {
       collapsed: state => state.serverSideStorage.prefsStorage.simple.collapseNav
     }),
     rootItems () {
-      return Object
-        .entries({ ...ROOT_ITEMS })
-        .map(([k, v]) => ({ ...v, name: k }))
-        .filter(({ criteria, anon, anonRoute }) => {
-          const set = new Set(criteria || [])
-          if (!this.federating && set.has('federating')) return false
-          if (this.private && set.has('!private')) return false
-          if (!this.currentUser && !(anon || anonRoute)) return false
-          if ((!this.currentUser || !this.currentUser.locked) && set.has('lockedUser')) return false
-          return true
-        })
+      return filterNavigation(
+        Object
+          .entries({ ...ROOT_ITEMS })
+          .map(([k, v]) => ({ ...v, name: k })),
+        {
+          isFederating: this.federating,
+          isPrivate: this.private,
+          currentUser: this.currentUser
+        }
+      )
     },
     pinnedList () {
-      return Object
-        .entries({ ...TIMELINES, ...ROOT_ITEMS })
-        .filter(([k]) => this.pinnedItems.has(k))
-        .map(([k, v]) => ({ ...v, name: k }))
-        .filter(({ criteria, anon, anonRoute }) => {
-          const set = new Set(criteria || [])
-          if (!this.federating && set.has('federating')) return false
-          if (this.private && set.has('!private')) return false
-          if (!this.currentUser && !(anon || anonRoute)) return false
-          if (this.currentUser && !this.currentUser.locked && set.has('locked')) return false
-          return true
-        })
+      return filterNavigation(
+        [
+          ...Object
+            .entries({
+              ...TIMELINES,
+              ...ROOT_ITEMS
+            })
+            .filter(([k]) => this.pinnedItems.has(k))
+            .map(([k, v]) => ({ ...v, name: k })),
+          ...this.lists.filter((k) => this.pinnedItems.has(k.name))
+
+        ],
+        {
+          isFederating: this.federating,
+          isPrivate: this.private,
+          currentUser: this.currentUser
+        }
+      )
     },
     ...mapGetters(['unreadChatCount'])
   }
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 99a4571e..767ba6ec 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -5,13 +5,18 @@
         <span>
           <span v-for="item in pinnedList" :key="item.name" class="pinned-item">
             <router-link
-              :to="{ name: (currentUser || item.anon) ? item.route : item.anonRoute, params: { username: currentUser.screen_name } }"
+              :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 "
+                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>
@@ -48,7 +53,9 @@
             v-show="showTimelines"
             class="timelines-background"
           >
-            <TimelineMenuContent class="timelines" :content="timelinesList" />
+            <ul class="timelines">
+              <NavigationEntry v-for="item in timelinesList" :key="item.name" :show-pin="true" :item="item" />
+            </ul>
           </div>
         </li>
         <li v-if="currentUser">
@@ -81,34 +88,10 @@
             v-show="showLists"
             class="timelines-background"
           >
-            <ListsMenuContent class="timelines" />
+            <ListsMenuContent :showPin="true" class="timelines" />
           </div>
         </li>
-        <li v-for="item in rootItems" :key="item.name">
-          <router-link
-            class="menu-item"
-            :to="{ name: (currentUser || item.anon) ? item.route : item.anonRoute, params: { username: currentUser.screen_name } }"
-          >
-            <FAIcon
-              fixed-width
-              class="fa-scale-110 fa-old-padding "
-              :icon="item.icon"
-            />{{ $t(item.label) }}
-            <button
-              type="button"
-              class="button-unstyled"
-              @click.stop.prevent="togglePin(item.name)"
-              >
-              <FAIcon
-                fixed-width
-                class="fa-scale-110 fa-old-padding "
-                :class="{ 'veryfaint': !isPinned(item.name) }"
-                :transform="!isPinned(item.name) ? 'rotate-45' : ''"
-                icon="thumbtack"
-              />
-            </button>
-          </router-link>
-        </li>
+        <NavigationEntry v-for="item in rootItems" :key="item.name" :show-pin="true" :item="item" />
       </ul>
     </div>
   </div>
@@ -220,16 +203,13 @@
     margin-right: 0.8em;
   }
 
-  .badge {
-    position: absolute;
-    right: 0.6rem;
-    top: 1.25em;
-  }
-
   .pinned-item {
-    .router-link-exact-active .svg-inline--fa {
-      color: $fallback--text;
-      color: var(--selectedMenuText, $fallback--text);
+    .router-link-active {
+      & .svg-inline--fa,
+      & .iconLetter {
+        color: $fallback--text;
+        color: var(--selectedMenuText, $fallback--text);
+      }
     }
   }
 }
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index 8f6cae66..ee590c4b 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -28,6 +28,7 @@ const Timeline = {
     'footerSlipgate' // reference to an element where we should put our footer
   ],
   data () {
+    console.log(this.timelineName)
     return {
       paused: false,
       unfocused: false,
diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue
index 627cafbb..fad3ff13 100644
--- a/src/components/timeline/timeline.vue
+++ b/src/components/timeline/timeline.vue
@@ -1,7 +1,7 @@
 <template>
   <div :class="['Timeline', classes.root]">
     <div :class="classes.header">
-      <TimelineMenu v-if="!embedded" />
+      <TimelineMenu v-if="!embedded" :timeline-name="timelineName"/>
       <button
         v-if="showLoadButton"
         class="button-default loadmore-button"
diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js
index 5a67b0a5..d74fbf4e 100644
--- a/src/components/timeline_menu/timeline_menu.js
+++ b/src/components/timeline_menu/timeline_menu.js
@@ -1,6 +1,8 @@
 import Popover from '../popover/popover.vue'
-import TimelineMenuContent from './timeline_menu_content.vue'
+import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
+import { ListsMenuContent } from '../lists_menu/lists_menu_content.vue'
 import { library } from '@fortawesome/fontawesome-svg-core'
+import { TIMELINES } from 'src/components/navigation/navigation.js'
 import {
   faChevronDown
 } from '@fortawesome/free-solid-svg-icons'
@@ -22,11 +24,13 @@ export const timelineNames = () => {
 const TimelineMenu = {
   components: {
     Popover,
-    TimelineMenuContent
+    NavigationEntry,
+    ListsMenuContent
   },
   data () {
     return {
-      isOpen: false
+      isOpen: false,
+      timelinesList: Object.entries(TIMELINES).map(([k, v]) => ({ ...v, name: k }))
     }
   },
   created () {
@@ -34,6 +38,12 @@ const TimelineMenu = {
       this.$store.dispatch('setLastTimeline', this.$route.name)
     }
   },
+  computed: {
+    useListsMenu () {
+      const route = this.$route.name
+      return route === 'lists-timeline'
+    }
+  },
   methods: {
     openMenu () {
       // $nextTick is too fast, animation won't play back but
diff --git a/src/components/timeline_menu/timeline_menu.vue b/src/components/timeline_menu/timeline_menu.vue
index 5ef96328..9acaff46 100644
--- a/src/components/timeline_menu/timeline_menu.vue
+++ b/src/components/timeline_menu/timeline_menu.vue
@@ -10,7 +10,10 @@
     @close="() => isOpen = false"
   >
     <template #content>
-      <TimelineMenuContent :content="timelinesList" />
+    <ListsMenuContent v-if="useListsMenu" :showPin="false" class="timelines" />
+    <ul v-else>
+      <NavigationEntry v-for="item in timelinesList" :key="item.name" :show-pin="false" :item="item" />
+    </ul>
     </template>
     <template #trigger>
       <span class="button-unstyled title timeline-menu-title">
@@ -138,8 +141,7 @@
       background-color: $fallback--lightBg;
       background-color: var(--selectedMenu, $fallback--lightBg);
       color: $fallback--text;
-      color: var(--selectedMenuText, $fallback--text);
-      --faint: var(--selectedMenuFaintText, $fallback--faint);
+      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);
diff --git a/src/components/timeline_menu/timeline_menu_content.js b/src/components/timeline_menu/timeline_menu_content.js
deleted file mode 100644
index c5b1cff2..00000000
--- a/src/components/timeline_menu/timeline_menu_content.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import { mapState } from 'vuex'
-import { library } from '@fortawesome/fontawesome-svg-core'
-import {
-  faUsers,
-  faGlobe,
-  faBookmark,
-  faEnvelope,
-  faHome
-} from '@fortawesome/free-solid-svg-icons'
-
-library.add(
-  faUsers,
-  faGlobe,
-  faBookmark,
-  faEnvelope,
-  faHome
-)
-
-const TimelineMenuContent = {
-  props: ['content'],
-  methods: {
-    isPinned (item) {
-      return this.pinnedItems.has(item)
-    },
-    togglePin (item) {
-      if (this.isPinned(item)) {
-        this.$store.commit('removeCollectionPreference', { path: 'collections.pinnedNavItems', value: item })
-      } else {
-        this.$store.commit('addCollectionPreference', { path: 'collections.pinnedNavItems', value: item })
-      }
-    }
-  },
-  computed: {
-    ...mapState({
-      currentUser: state => state.users.currentUser,
-      privateMode: state => state.instance.private,
-      federating: state => state.instance.federating,
-      pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems)
-    }),
-    list () {
-      return (this.content || []).filter(({ criteria, anon, anonRoute }) => {
-        const set = new Set(criteria || [])
-        if (!this.federating && set.has('federating')) return false
-        if (this.private && set.has('!private')) return false
-        if (!this.currentUser && !(anon || anonRoute)) return false
-        return true
-      })
-    }
-  }
-}
-
-export default TimelineMenuContent
diff --git a/src/components/timeline_menu/timeline_menu_content.vue b/src/components/timeline_menu/timeline_menu_content.vue
deleted file mode 100644
index 7dc76b62..00000000
--- a/src/components/timeline_menu/timeline_menu_content.vue
+++ /dev/null
@@ -1,31 +0,0 @@
-<template>
-  <ul>
-    <li v-for="item in list" :key="item.name">
-      <router-link
-        class="menu-item"
-        :to="{ name: (currentUser || item.anon) ? item.route : item.anonRoute, params: { username: currentUser.screen_name } }"
-      >
-        <FAIcon
-          fixed-width
-          class="fa-scale-110 fa-old-padding "
-          :icon="item.icon"
-        />{{ $t(item.label) }}
-        <button
-          type="button"
-          class="button-unstyled"
-          @click.stop.prevent="togglePin(item.name)"
-          >
-          <FAIcon
-            fixed-width
-            class="fa-scale-110 fa-old-padding "
-            :class="{ 'veryfaint': !isPinned(item.name) }"
-            :transform="!isPinned(item.name) ? 'rotate-45' : ''"
-            icon="thumbtack"
-          />
-        </button>
-      </router-link>
-    </li>
-  </ul>
-</template>
-
-<script src="./timeline_menu_content.js"></script>
-- 
cgit v1.2.3-70-g09d2


From 3a16a59f37b9b637bb4cbc1c3575810a65515cbc Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Thu, 11 Aug 2022 21:56:30 +0300
Subject: navigation refactored, used in mobile nav as well

---
 src/components/mobile_nav/mobile_nav.js        |  9 ++-
 src/components/mobile_nav/mobile_nav.vue       | 29 ++++----
 src/components/nav_panel/nav_panel.js          | 25 ++-----
 src/components/nav_panel/nav_panel.vue         | 28 +-------
 src/components/navigation/filter.js            | 11 +++
 src/components/navigation/navigation.js        | 61 ++++++++++++++++
 src/components/navigation/navigation_entry.js  | 32 +++++++++
 src/components/navigation/navigation_entry.vue | 96 ++++++++++++++++++++++++++
 src/components/navigation/navigation_pins.js   | 68 ++++++++++++++++++
 src/components/navigation/navigation_pins.vue  | 64 +++++++++++++++++
 src/modules/api.js                             |  3 +
 11 files changed, 363 insertions(+), 63 deletions(-)
 create mode 100644 src/components/navigation/filter.js
 create mode 100644 src/components/navigation/navigation.js
 create mode 100644 src/components/navigation/navigation_entry.js
 create mode 100644 src/components/navigation/navigation_entry.vue
 create mode 100644 src/components/navigation/navigation_pins.js
 create mode 100644 src/components/navigation/navigation_pins.vue

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js
index 877d52a9..af47f032 100644
--- 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
index 949cf17e..82e726a4 100644
--- 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
index cdfe996e..7daa5e6a 100644
--- 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
index 767ba6ec..6e89094a 100644
--- 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
new file mode 100644
index 00000000..b41892a4
--- /dev/null
+++ 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
new file mode 100644
index 00000000..f37d37fe
--- /dev/null
+++ 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
new file mode 100644
index 00000000..09c216ed
--- /dev/null
+++ 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
new file mode 100644
index 00000000..7d761395
--- /dev/null
+++ 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
new file mode 100644
index 00000000..c71f6d24
--- /dev/null
+++ 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
new file mode 100644
index 00000000..754aad7a
--- /dev/null
+++ 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
index 80a978f9..f783fa4f 100644
--- 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
-- 
cgit v1.2.3-70-g09d2


From 0123872b56ccd2d534913706ae0f27ea8d6481de Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Fri, 12 Aug 2022 00:50:08 +0300
Subject: fixes + fixes for anon users

---
 src/components/mobile_nav/mobile_nav.vue          |  2 +-
 src/components/nav_panel/nav_panel.js             | 13 +++++++++++++
 src/components/nav_panel/nav_panel.vue            |  2 +-
 src/components/navigation/navigation.js           |  1 -
 src/components/navigation/navigation_entry.vue    |  2 +-
 src/components/navigation/navigation_pins.js      |  7 +++++++
 src/components/navigation/navigation_pins.vue     |  1 +
 test/unit/specs/modules/serverSideStorage.spec.js |  4 ++--
 8 files changed, 26 insertions(+), 6 deletions(-)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue
index 82e726a4..01a77589 100644
--- a/src/components/mobile_nav/mobile_nav.vue
+++ b/src/components/mobile_nav/mobile_nav.vue
@@ -86,6 +86,7 @@
     grid-template-columns: 2fr auto;
     width: 100%;
     box-sizing: border-box;
+
     a {
       color: var(--topBarLink, $fallback--link);
     }
@@ -175,7 +176,6 @@
 
     .pinned-item {
       flex-grow: 1;
-      text-align: center;
     }
   }
 
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index 7daa5e6a..b4e1ec0b 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -86,6 +86,19 @@ const NavPanel = {
       pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems),
       collapsed: state => state.serverSideStorage.prefsStorage.simple.collapseNav
     }),
+    timelinesItems () {
+      return filterNavigation(
+        Object
+          .entries({ ...TIMELINES })
+          .map(([k, v]) => ({ ...v, name: k })),
+        {
+          hasChats: this.pleromaChatMessagesAvailable,
+          isFederating: this.federating,
+          isPrivate: this.private,
+          currentUser: this.currentUser
+        }
+      )
+    },
     rootItems () {
       return filterNavigation(
         Object
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 6e89094a..84b000a4 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -37,7 +37,7 @@
             class="timelines-background"
           >
             <ul class="timelines">
-              <NavigationEntry v-for="item in timelinesList" :key="item.name" :show-pin="true" :item="item" />
+              <NavigationEntry v-for="item in timelinesItems" :key="item.name" :show-pin="true" :item="item" />
             </ul>
           </div>
         </li>
diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js
index f37d37fe..57db2253 100644
--- a/src/components/navigation/navigation.js
+++ b/src/components/navigation/navigation.js
@@ -1,7 +1,6 @@
 export const TIMELINES = {
   home: {
     route: 'friends',
-    anonRoute: 'public-timeline',
     icon: 'home',
     label: 'nav.home_timeline',
     criteria: ['!private']
diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue
index 7d761395..0dcf5d85 100644
--- a/src/components/navigation/navigation_entry.vue
+++ b/src/components/navigation/navigation_entry.vue
@@ -21,7 +21,7 @@
       @click.stop.prevent="togglePin(item.name)"
       >
       <FAIcon
-        v-if="showPin"
+        v-if="showPin && currentUser"
         fixed-width
         class="fa-scale-110"
         :class="{ 'veryfaint': !isPinned(item.name) }"
diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js
index c71f6d24..8a892466 100644
--- a/src/components/navigation/navigation_pins.js
+++ b/src/components/navigation/navigation_pins.js
@@ -42,6 +42,13 @@ const NavPanel = {
       pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems)
     }),
     pinnedList () {
+      if (!this.currentUser) {
+        return [
+          { ...TIMELINES.public, name: 'public' },
+          { ...TIMELINES.twkn, name: 'twkn' },
+          { ...ROOT_ITEMS.about, name: 'about' }
+        ]
+      }
       return filterNavigation(
         [
           ...Object
diff --git a/src/components/navigation/navigation_pins.vue b/src/components/navigation/navigation_pins.vue
index 754aad7a..f421b2be 100644
--- a/src/components/navigation/navigation_pins.vue
+++ b/src/components/navigation/navigation_pins.vue
@@ -46,6 +46,7 @@
     position: relative;
     flex: 0 0 3em;
     min-width: 2em;
+    text-align: center;
 
     & .svg-inline--fa,
     & .iconLetter {
diff --git a/test/unit/specs/modules/serverSideStorage.spec.js b/test/unit/specs/modules/serverSideStorage.spec.js
index ada3b7ca..edb23e8a 100644
--- a/test/unit/specs/modules/serverSideStorage.spec.js
+++ b/test/unit/specs/modules/serverSideStorage.spec.js
@@ -116,7 +116,7 @@ describe('The serverSideStorage module', () => {
         expect(state.prefsStorage._journal.length).to.eql(1)
         expect(state.prefsStorage._journal[0]).to.eql({
           path: 'simple.testing',
-          command: 'set',
+          operation: 'set',
           args: [1],
           // should have A timestamp, we don't really care what it is
           timestamp: state.prefsStorage._journal[0].timestamp
@@ -132,7 +132,7 @@ describe('The serverSideStorage module', () => {
         expect(state.prefsStorage._journal.length).to.eql(1)
         expect(state.prefsStorage._journal[0]).to.eql({
           path: 'simple.testing',
-          command: 'set',
+          operation: 'set',
           args: [2],
           // should have A timestamp, we don't really care what it is
           timestamp: state.prefsStorage._journal[0].timestamp
-- 
cgit v1.2.3-70-g09d2


From c3e7eadddff2357f51b49bb8eb8125777d754a0e Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Fri, 12 Aug 2022 01:00:41 +0300
Subject: let mobile users customize top bar as well

---
 src/boot/routes.js                         |  4 +++-
 src/components/nav_panel/nav_panel.js      |  1 +
 src/components/nav_panel/nav_panel.vue     |  7 +++++--
 src/components/side_drawer/side_drawer.js  |  2 ++
 src/components/side_drawer/side_drawer.vue | 12 ++++++++++++
 src/i18n/en.json                           |  3 ++-
 6 files changed, 25 insertions(+), 4 deletions(-)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/boot/routes.js b/src/boot/routes.js
index 95e47199..178bb937 100644
--- a/src/boot/routes.js
+++ b/src/boot/routes.js
@@ -23,6 +23,7 @@ import RemoteUserResolver from 'components/remote_user_resolver/remote_user_reso
 import Lists from 'components/lists/lists.vue'
 import ListsTimeline from 'components/lists_timeline/lists_timeline.vue'
 import ListsEdit from 'components/lists_edit/lists_edit.vue'
+import NavPanel from 'src/components/nav_panel/nav_panel.vue'
 
 export default (store) => {
   const validateAuthenticatedRoute = (to, from, next) => {
@@ -78,7 +79,8 @@ export default (store) => {
     { name: 'user-profile', path: '/:_(users)?/:name', component: UserProfile },
     { name: 'lists', path: '/lists', component: Lists },
     { name: 'lists-timeline', path: '/lists/:id', component: ListsTimeline },
-    { name: 'lists-edit', path: '/lists/:id/edit', component: ListsEdit }
+    { name: 'lists-edit', path: '/lists/:id/edit', component: ListsEdit },
+    { name: 'edit-navigation', path: '/nav-edit', component: NavPanel, props: () => ({ forceExpand: true }), beforeEnter: validateAuthenticatedRoute }
   ]
 
   if (store.state.instance.pleromaChatMessagesAvailable) {
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index b4e1ec0b..0d71a924 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -34,6 +34,7 @@ library.add(
   faList
 )
 const NavPanel = {
+  props: ['forceExpand'],
   created () {
     if (this.currentUser && this.currentUser.locked) {
       this.$store.dispatch('startFetchingFollowRequests')
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 84b000a4..5895473d 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -1,7 +1,10 @@
 <template>
   <div class="NavPanel">
     <div class="panel panel-default">
-      <div class="panel-heading">
+      <div
+        v-if="!forceExpand"
+        class="panel-heading"
+      >
         <NavigationPins />
         <div class="spacer"/>
         <button
@@ -15,7 +18,7 @@
           />
         </button>
       </div>
-      <ul class="panel-body" v-if="!collapsed">
+      <ul class="panel-body" v-if="!collapsed || forceExpand">
         <li v-if="currentUser || !privateMode">
           <button
             class="button-unstyled menu-item"
diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js
index 913fa695..cbfd6b7d 100644
--- a/src/components/side_drawer/side_drawer.js
+++ b/src/components/side_drawer/side_drawer.js
@@ -15,6 +15,7 @@ import {
   faTachometerAlt,
   faCog,
   faInfoCircle,
+  faCompass,
   faList
 } from '@fortawesome/free-solid-svg-icons'
 
@@ -30,6 +31,7 @@ library.add(
   faTachometerAlt,
   faCog,
   faInfoCircle,
+  faCompass,
   faList
 )
 
diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue
index 7d9d36d7..5223fdfa 100644
--- a/src/components/side_drawer/side_drawer.vue
+++ b/src/components/side_drawer/side_drawer.vue
@@ -191,6 +191,18 @@
             /> {{ $t("nav.administration") }}
           </a>
         </li>
+        <li
+          v-if="currentUser"
+          @click="toggleDrawer"
+        >
+          <router-link :to="{ name: 'edit-navigation' }">
+            <FAIcon
+              fixed-width
+              class="fa-scale-110 fa-old-padding"
+              icon="compass"
+            /> {{ $t("nav.edit_nav_mobile") }}
+          </router-link>
+        </li>
         <li
           v-if="currentUser"
           @click="toggleDrawer"
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 2e845959..f042d562 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -149,7 +149,8 @@
     "preferences": "Preferences",
     "timelines": "Timelines",
     "chats": "Chats",
-    "lists": "Lists"
+    "lists": "Lists",
+    "edit_nav_mobile": "Customize navigation bar"
   },
   "notifications": {
     "broken_favorite": "Unknown status, searching for it…",
-- 
cgit v1.2.3-70-g09d2


From 7d13f38d8807fb090397af159b485a195c379fb3 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Fri, 12 Aug 2022 01:26:19 +0300
Subject: limit amount of pins on desktop to 6 (for now)

---
 src/components/nav_panel/nav_panel.vue       | 2 +-
 src/components/navigation/navigation_pins.js | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 5895473d..0da5379a 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -5,7 +5,7 @@
         v-if="!forceExpand"
         class="panel-heading"
       >
-        <NavigationPins />
+        <NavigationPins :limit="6" />
         <div class="spacer"/>
         <button
           class="button-unstyled"
diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js
index 43be4275..59ad808b 100644
--- a/src/components/navigation/navigation_pins.js
+++ b/src/components/navigation/navigation_pins.js
@@ -28,6 +28,7 @@ library.add(
   faList
 )
 const NavPanel = {
+  props: ['limit'],
   computed: {
     getters () {
       return this.$store.getters
@@ -67,7 +68,7 @@ const NavPanel = {
           isPrivate: this.privateMode,
           currentUser: this.currentUser
         }
-      )
+      ).slice(0, this.limit)
     }
   }
 }
-- 
cgit v1.2.3-70-g09d2


From 80600137d1be64c83737ca3296f4aaae1033870c Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Fri, 12 Aug 2022 01:27:09 +0300
Subject: lint

---
 src/components/desktop_nav/desktop_nav.vue       |  2 +-
 src/components/lists_menu/lists_menu_content.vue | 11 ++--
 src/components/mobile_nav/mobile_nav.vue         |  2 +-
 src/components/nav_panel/nav_panel.vue           | 36 +++++++++----
 src/components/navigation/navigation_entry.vue   | 64 ++++++++++++------------
 src/components/navigation/navigation_pins.vue    | 12 +++--
 src/components/search_bar/search_bar.vue         |  4 +-
 src/components/timeline/timeline.vue             |  5 +-
 src/components/timeline_menu/timeline_menu.vue   | 17 +++++--
 9 files changed, 94 insertions(+), 59 deletions(-)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/desktop_nav/desktop_nav.vue b/src/components/desktop_nav/desktop_nav.vue
index bbb22707..5db7fc79 100644
--- a/src/components/desktop_nav/desktop_nav.vue
+++ b/src/components/desktop_nav/desktop_nav.vue
@@ -61,7 +61,7 @@
             :title="$t('nav.administration')"
           />
         </a>
-        <span class="spacer"/>
+        <span class="spacer" />
         <button
           v-if="currentUser"
           class="button-unstyled nav-icon"
diff --git a/src/components/lists_menu/lists_menu_content.vue b/src/components/lists_menu/lists_menu_content.vue
index 9c58b092..f93e80c9 100644
--- a/src/components/lists_menu/lists_menu_content.vue
+++ b/src/components/lists_menu/lists_menu_content.vue
@@ -1,7 +1,12 @@
 <template>
-<ul>
-  <NavigationEntry v-for="item in lists" :key="item.name" :show-pin="showPin" :item="item" />
-</ul>
+  <ul>
+    <NavigationEntry
+      v-for="item in lists"
+      :key="item.name"
+      :show-pin="showPin"
+      :item="item"
+    />
+  </ul>
 </template>
 
 <script src="./lists_menu_content.js"></script>
diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue
index 01a77589..9152879c 100644
--- a/src/components/mobile_nav/mobile_nav.vue
+++ b/src/components/mobile_nav/mobile_nav.vue
@@ -21,7 +21,7 @@
             class="alert-dot"
           />
         </button>
-        <NavigationPins class="pins"/>
+        <NavigationPins class="pins" />
       </div> <div class="item right">
         <button
           v-if="currentUser"
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 0da5379a..077f97fc 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -6,7 +6,7 @@
         class="panel-heading"
       >
         <NavigationPins :limit="6" />
-        <div class="spacer"/>
+        <div class="spacer" />
         <button
           class="button-unstyled"
           @click="toggleCollapse"
@@ -18,7 +18,10 @@
           />
         </button>
       </div>
-      <ul class="panel-body" v-if="!collapsed || forceExpand">
+      <ul
+        v-if="!collapsed || forceExpand"
+        class="panel-body"
+      >
         <li v-if="currentUser || !privateMode">
           <button
             class="button-unstyled menu-item"
@@ -40,7 +43,12 @@
             class="timelines-background"
           >
             <ul class="timelines">
-              <NavigationEntry v-for="item in timelinesItems" :key="item.name" :show-pin="true" :item="item" />
+              <NavigationEntry
+                v-for="item in timelinesItems"
+                :key="item.name"
+                :show-pin="true"
+                :item="item"
+              />
             </ul>
           </div>
         </li>
@@ -49,11 +57,11 @@
             class="button-unstyled menu-item"
             @click="toggleLists"
           >
-              <FAIcon
-                fixed-width
-                class="fa-scale-110"
-                icon="list"
-              />{{ $t("nav.lists") }}
+            <FAIcon
+              fixed-width
+              class="fa-scale-110"
+              icon="list"
+            />{{ $t("nav.lists") }}
             <FAIcon
               class="timelines-chevron"
               fixed-width
@@ -74,10 +82,18 @@
             v-show="showLists"
             class="timelines-background"
           >
-            <ListsMenuContent :showPin="true" class="timelines" />
+            <ListsMenuContent
+              :show-pin="true"
+              class="timelines"
+            />
           </div>
         </li>
-        <NavigationEntry v-for="item in rootItems" :key="item.name" :show-pin="true" :item="item" />
+        <NavigationEntry
+          v-for="item in rootItems"
+          :key="item.name"
+          :show-pin="true"
+          :item="item"
+        />
       </ul>
     </div>
   </div>
diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue
index 0dcf5d85..64601be6 100644
--- a/src/components/navigation/navigation_entry.vue
+++ b/src/components/navigation/navigation_entry.vue
@@ -1,42 +1,42 @@
 <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 } }"
+  <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 && currentUser"
+        v-if="item.icon"
         fixed-width
         class="fa-scale-110"
-        :class="{ 'veryfaint': !isPinned(item.name) }"
-        :transform="!isPinned(item.name) ? 'rotate-45' : ''"
-        icon="thumbtack"
+        :icon="item.icon"
+      />
+      <span
+        v-if="item.iconLetter"
+        class="icon iconLetter fa-scale-110"
+      >{{ item.iconLetter }}
+      </span>{{ item.labelRaw || $t(item.label) }}
+      <button
+        type="button"
+        class="button-unstyled"
+        @click.stop.prevent="togglePin(item.name)"
+      >
+        <FAIcon
+          v-if="showPin && currentUser"
+          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"
+        <div
+          v-if="item.badgeGetter && getters[item.badgeGetter]"
+          class="badge badge-notification"
         >
-        {{ getters[item.badgeGetter] }}
-      </div>
-    </button>
-  </router-link>
-</li>
+          {{ getters[item.badgeGetter] }}
+        </div>
+      </button>
+    </router-link>
+  </li>
 </template>
 
 <script src="./navigation_entry.js"></script>
diff --git a/src/components/navigation/navigation_pins.vue b/src/components/navigation/navigation_pins.vue
index f421b2be..fc89ca3d 100644
--- a/src/components/navigation/navigation_pins.vue
+++ b/src/components/navigation/navigation_pins.vue
@@ -1,22 +1,24 @@
 <template>
   <span class="NavigationPins">
     <router-link
-      v-for="item in pinnedList" :key="item.name" class="pinned-item"
+      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>
+      >{{ item.iconLetter }}</span>
       <div
         v-if="item.badgeGetter && getters[item.badgeGetter]"
         class="alert-dot"
-        />
+      />
     </router-link>
   </span>
 </template>
diff --git a/src/components/search_bar/search_bar.vue b/src/components/search_bar/search_bar.vue
index 14842835..199a7500 100644
--- a/src/components/search_bar/search_bar.vue
+++ b/src/components/search_bar/search_bar.vue
@@ -47,8 +47,8 @@
           class="cancel-icon fa-scale-110 fa-old-padding"
         />
       </button>
-      <span class="spacer"/>
-      <span class="spacer"/>
+      <span class="spacer" />
+      <span class="spacer" />
     </template>
   </div>
 </template>
diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue
index fad3ff13..f842240b 100644
--- a/src/components/timeline/timeline.vue
+++ b/src/components/timeline/timeline.vue
@@ -1,7 +1,10 @@
 <template>
   <div :class="['Timeline', classes.root]">
     <div :class="classes.header">
-      <TimelineMenu v-if="!embedded" :timeline-name="timelineName"/>
+      <TimelineMenu
+        v-if="!embedded"
+        :timeline-name="timelineName"
+      />
       <button
         v-if="showLoadButton"
         class="button-default loadmore-button"
diff --git a/src/components/timeline_menu/timeline_menu.vue b/src/components/timeline_menu/timeline_menu.vue
index 9acaff46..e7250282 100644
--- a/src/components/timeline_menu/timeline_menu.vue
+++ b/src/components/timeline_menu/timeline_menu.vue
@@ -10,10 +10,19 @@
     @close="() => isOpen = false"
   >
     <template #content>
-    <ListsMenuContent v-if="useListsMenu" :showPin="false" class="timelines" />
-    <ul v-else>
-      <NavigationEntry v-for="item in timelinesList" :key="item.name" :show-pin="false" :item="item" />
-    </ul>
+      <ListsMenuContent
+        v-if="useListsMenu"
+        :show-pin="false"
+        class="timelines"
+      />
+      <ul v-else>
+        <NavigationEntry
+          v-for="item in timelinesList"
+          :key="item.name"
+          :show-pin="false"
+          :item="item"
+        />
+      </ul>
     </template>
     <template #trigger>
       <span class="button-unstyled title timeline-menu-title">
-- 
cgit v1.2.3-70-g09d2


From 14292d7ed12a806efcf766895bc1c3aa56fd53f8 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Mon, 15 Aug 2022 21:56:07 +0300
Subject: edit mode

---
 src/boot/routes.js                     |  2 +-
 src/components/nav_panel/nav_panel.js  | 10 ++++++++--
 src/components/nav_panel/nav_panel.vue | 14 +++++++++++---
 src/i18n/en.json                       |  3 ++-
 4 files changed, 22 insertions(+), 7 deletions(-)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/boot/routes.js b/src/boot/routes.js
index 178bb937..39372ab9 100644
--- a/src/boot/routes.js
+++ b/src/boot/routes.js
@@ -80,7 +80,7 @@ export default (store) => {
     { name: 'lists', path: '/lists', component: Lists },
     { name: 'lists-timeline', path: '/lists/:id', component: ListsTimeline },
     { name: 'lists-edit', path: '/lists/:id/edit', component: ListsEdit },
-    { name: 'edit-navigation', path: '/nav-edit', component: NavPanel, props: () => ({ forceExpand: true }), beforeEnter: validateAuthenticatedRoute }
+    { name: 'edit-navigation', path: '/nav-edit', component: NavPanel, props: () => ({ forceExpand: true, forceEditMode: true }), beforeEnter: validateAuthenticatedRoute }
   ]
 
   if (store.state.instance.pleromaChatMessagesAvailable) {
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index b11a258b..1e68561f 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -4,6 +4,7 @@ import { TIMELINES, ROOT_ITEMS } from 'src/components/navigation/navigation.js'
 import { getListEntries, 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 Checkbox from 'src/components/checkbox/checkbox.vue'
 
 import { library } from '@fortawesome/fontawesome-svg-core'
 import {
@@ -34,16 +35,18 @@ library.add(
   faList
 )
 const NavPanel = {
-  props: ['forceExpand'],
+  props: ['forceExpand', 'forceEditMode'],
   created () {
   },
   components: {
     ListsMenuContent,
     NavigationEntry,
-    NavigationPins
+    NavigationPins,
+    Checkbox
   },
   data () {
     return {
+      editMode: false,
       showTimelines: false,
       showLists: false,
       timelinesList: Object.entries(TIMELINES).map(([k, v]) => ({ ...v, name: k })),
@@ -57,6 +60,9 @@ const NavPanel = {
     toggleLists () {
       this.showLists = !this.showLists
     },
+    toggleEditMode () {
+      this.editMode = !this.editMode
+    },
     toggleCollapse () {
       this.$store.commit('setPreference', { path: 'simple.collapseNav', value: !this.collapsed })
       this.$store.dispatch('pushServerSideStorage')
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 077f97fc..01a407c1 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -46,7 +46,7 @@
               <NavigationEntry
                 v-for="item in timelinesItems"
                 :key="item.name"
-                :show-pin="true"
+                :show-pin="editMode"
                 :item="item"
               />
             </ul>
@@ -83,7 +83,7 @@
             class="timelines-background"
           >
             <ListsMenuContent
-              :show-pin="true"
+              :show-pin="editMode"
               class="timelines"
             />
           </div>
@@ -91,9 +91,17 @@
         <NavigationEntry
           v-for="item in rootItems"
           :key="item.name"
-          :show-pin="true"
+          :show-pin="editMode || forceEditMode"
           :item="item"
         />
+        <div
+          v-if="!forceEditMode"
+          class="panel-footer"
+        >
+          <Checkbox v-model="editMode">
+            {{ $t('nav.edit_pinned') }}
+          </Checkbox>
+        </div>
       </ul>
     </div>
   </div>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index f042d562..a7c86e6c 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -150,7 +150,8 @@
     "timelines": "Timelines",
     "chats": "Chats",
     "lists": "Lists",
-    "edit_nav_mobile": "Customize navigation bar"
+    "edit_nav_mobile": "Customize navigation bar",
+    "edit_pinned": "Edit pinned items"
   },
   "notifications": {
     "broken_favorite": "Unknown status, searching for it…",
-- 
cgit v1.2.3-70-g09d2


From f12198c2b3245188b2e23696de2cfb2d088cc690 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Wed, 17 Aug 2022 21:13:39 +0300
Subject: fix edit mode checkbox showing up for anon users

---
 src/components/nav_panel/nav_panel.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 01a407c1..e089f0be 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -95,7 +95,7 @@
           :item="item"
         />
         <div
-          v-if="!forceEditMode"
+          v-if="!forceEditMode && currentUser"
           class="panel-footer"
         >
           <Checkbox v-model="editMode">
-- 
cgit v1.2.3-70-g09d2


From b4409822204a287ef5d3a9f7dcc26ae4a8ad5d2a Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Wed, 17 Aug 2022 21:14:10 +0300
Subject: fix lists not having pins in edit mode

---
 src/components/nav_panel/nav_panel.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index e089f0be..67b124dc 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -83,7 +83,7 @@
             class="timelines-background"
           >
             <ListsMenuContent
-              :show-pin="editMode"
+              :show-pin="editMode || forceEditMode"
               class="timelines"
             />
           </div>
-- 
cgit v1.2.3-70-g09d2


From fa41f6cf969456316b5265df0bb550770624d7ba Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 23 Aug 2022 00:50:38 +0300
Subject: add an underline to the selected pin to improve accesibility

---
 src/components/nav_panel/nav_panel.vue        | 9 +++++++--
 src/components/navigation/navigation_pins.vue | 9 +++++++--
 src/panel.scss                                | 4 ++--
 3 files changed, 16 insertions(+), 6 deletions(-)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 67b124dc..11260818 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -3,7 +3,7 @@
     <div class="panel panel-default">
       <div
         v-if="!forceExpand"
-        class="panel-heading"
+        class="panel-heading nav-panel-heading"
       >
         <NavigationPins :limit="6" />
         <div class="spacer" />
@@ -192,6 +192,7 @@
 
   .timelines-chevron {
     margin-left: 0.8em;
+    margin-right: 0.8em;
     font-size: 1.1em;
   }
 
@@ -209,9 +210,13 @@
     background-color: var(--bg, $fallback--bg);
   }
 
+  .nav-panel-heading {
+   // breaks without a unit
+   --panel-heading-height-padding: 0em;
+  }
+
   .fa-scale-110 {
     margin-right: 0.8em;
   }
-
 }
 </style>
diff --git a/src/components/navigation/navigation_pins.vue b/src/components/navigation/navigation_pins.vue
index fc89ca3d..7942f23c 100644
--- a/src/components/navigation/navigation_pins.vue
+++ b/src/components/navigation/navigation_pins.vue
@@ -30,6 +30,7 @@
 .NavigationPins {
   display: flex;
   overflow: hidden;
+  height: 100%;
 
   .alert-dot {
     border-radius: 100%;
@@ -49,6 +50,7 @@
     flex: 0 0 3em;
     min-width: 2em;
     text-align: center;
+    overflow: visible;
 
     & .svg-inline--fa,
     & .iconLetter {
@@ -56,10 +58,13 @@
     }
 
     &.router-link-active {
+      color: $fallback--text;
+      color: var(--selectedMenuText, $fallback--text);
+      border-bottom: 4px solid;
+
       & .svg-inline--fa,
       & .iconLetter {
-        color: $fallback--text;
-        color: var(--selectedMenuText, $fallback--text);
+        color: inherit;
       }
     }
   }
diff --git a/src/panel.scss b/src/panel.scss
index 3a814269..1022758a 100644
--- a/src/panel.scss
+++ b/src/panel.scss
@@ -46,7 +46,7 @@
 .panel-footer {
   --panel-heading-height-padding: 0.6em;
   --__panel-heading-height: 3.2em;
-  --__panel-heading-height-inner: calc(var(--__panel-heading-height) - 2 * var(--panel-heading-height-padding));
+  --__panel-heading-height-inner: calc(var(--__panel-heading-height) - 2 * var(--panel-heading-height-padding, 0));
 
   position: relative;
   box-sizing: border-box;
@@ -57,7 +57,7 @@
   grid-column-gap: 0.5em;
   flex: none;
   background-size: cover;
-  padding: 0.6em;
+  padding: var(--panel-heading-height-padding);
   height: var(--__panel-heading-height);
   line-height: var(--__panel-heading-height-inner);
   z-index: 4;
-- 
cgit v1.2.3-70-g09d2


From f44ae0ce6b689c4dab7f6ae5105ea5b23a909de8 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 23 Aug 2022 22:15:46 +0300
Subject: aria

---
 src/components/nav_panel/nav_panel.vue | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 11260818..b4ca6022 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -26,6 +26,7 @@
           <button
             class="button-unstyled menu-item"
             @click="toggleTimelines"
+            :aria-expanded="showTimelines ? 'true' : 'false'"
           >
             <FAIcon
               fixed-width
@@ -56,6 +57,7 @@
           <button
             class="button-unstyled menu-item"
             @click="toggleLists"
+            :aria-expanded="showLists ? 'true' : 'false'"
           >
             <FAIcon
               fixed-width
-- 
cgit v1.2.3-70-g09d2


From d3454ca7de6b869b13d98020dd1a567516e77305 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 23 Aug 2022 22:16:51 +0300
Subject: fix being unable to edit timeline pins on mobile

---
 src/components/nav_panel/nav_panel.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index b4ca6022..b53f1baf 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -47,7 +47,7 @@
               <NavigationEntry
                 v-for="item in timelinesItems"
                 :key="item.name"
-                :show-pin="editMode"
+                :show-pin="editMode || forceEditMode"
                 :item="item"
               />
             </ul>
-- 
cgit v1.2.3-70-g09d2


From dc5992f990aea1d3b780ae903d361092e815da54 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 23 Aug 2022 22:18:33 +0300
Subject: lint

---
 src/components/nav_panel/nav_panel.vue    | 4 ++--
 src/components/side_drawer/side_drawer.js | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index b53f1baf..32c9bed5 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -25,8 +25,8 @@
         <li v-if="currentUser || !privateMode">
           <button
             class="button-unstyled menu-item"
-            @click="toggleTimelines"
             :aria-expanded="showTimelines ? 'true' : 'false'"
+            @click="toggleTimelines"
           >
             <FAIcon
               fixed-width
@@ -56,8 +56,8 @@
         <li v-if="currentUser">
           <button
             class="button-unstyled menu-item"
-            @click="toggleLists"
             :aria-expanded="showLists ? 'true' : 'false'"
+            @click="toggleLists"
           >
             <FAIcon
               fixed-width
diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js
index d5dabb72..bb22446b 100644
--- a/src/components/side_drawer/side_drawer.js
+++ b/src/components/side_drawer/side_drawer.js
@@ -89,7 +89,7 @@ const SideDrawer = {
       }
       name = this.currentUser ? 'friends' : 'public-timeline'
       if (USERNAME_ROUTES.has(name)) {
-        return { name, params: { username: this.currentUser.screen_name }}
+        return { name, params: { username: this.currentUser.screen_name } }
       } else {
         return { name }
       }
-- 
cgit v1.2.3-70-g09d2


From 8c48a56a899aab57246fcfde0a28c1712de8b9da Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Wed, 24 Aug 2022 20:24:21 +0300
Subject: change ugly checkbox to a list element that doesn't look too much out
 of place

---
 src/components/nav_panel/nav_panel.vue | 19 +++++++++++++------
 src/i18n/en.json                       |  3 ++-
 2 files changed, 15 insertions(+), 7 deletions(-)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 32c9bed5..c88b019c 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -96,14 +96,21 @@
           :show-pin="editMode || forceEditMode"
           :item="item"
         />
-        <div
+        <li
           v-if="!forceEditMode && currentUser"
-          class="panel-footer"
         >
-          <Checkbox v-model="editMode">
-            {{ $t('nav.edit_pinned') }}
-          </Checkbox>
-        </div>
+          <button
+            class="menu-item button-unstyled"
+            @click="toggleEditMode"
+          >
+            <FAIcon
+              fixed-width
+              class="fa-scale-110"
+              :icon="editMode ? 'check' : 'wrench'"
+            />
+            {{ editMode ? $t('nav.edit_finish') : $t('nav.edit_pinned') }}
+          </button>
+        </li>
       </ul>
     </div>
   </div>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 68bf883a..8d61ba72 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -156,7 +156,8 @@
     "chats": "Chats",
     "lists": "Lists",
     "edit_nav_mobile": "Customize navigation bar",
-    "edit_pinned": "Edit pinned items"
+    "edit_pinned": "Edit pinned items",
+    "edit_finish": "Done editing"
   },
   "notifications": {
     "broken_favorite": "Unknown status, searching for it…",
-- 
cgit v1.2.3-70-g09d2


From 768b3ea14fa8f69192b39642701babcb94f80156 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 30 Aug 2022 00:15:42 +0300
Subject: refactor navigation-entry and use them in other nav items

---
 src/App.scss                                   |   9 +-
 src/components/nav_panel/nav_panel.vue         | 184 +++++++++----------------
 src/components/navigation/navigation_entry.js  |   1 +
 src/components/navigation/navigation_entry.vue |  74 ++++++----
 4 files changed, 118 insertions(+), 150 deletions(-)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/App.scss b/src/App.scss
index 9c08c6b2..75b2667c 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -132,11 +132,10 @@ i[class*=icon-],
 
 .button-unstyled:hover,
 a:hover {
-  i[class*=icon-],
-  .svg-inline--fa,
-  .iconLetter {
-    color: $fallback--text;
-    color: var(--text, $fallback--text);
+  > i[class*=icon-],
+  > .svg-inline--fa,
+  > .iconLetter {
+    color: var(--text);
   }
 }
 
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index c88b019c..5ee2b701 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -22,95 +22,77 @@
         v-if="!collapsed || forceExpand"
         class="panel-body"
       >
-        <li v-if="currentUser || !privateMode">
-          <button
-            class="button-unstyled menu-item"
-            :aria-expanded="showTimelines ? 'true' : 'false'"
-            @click="toggleTimelines"
-          >
-            <FAIcon
-              fixed-width
-              class="fa-scale-110"
-              icon="stream"
-            />{{ $t("nav.timelines") }}
-            <FAIcon
-              class="timelines-chevron"
-              fixed-width
-              :icon="showTimelines ? 'chevron-up' : 'chevron-down'"
+        <NavigationEntry
+          v-if="currentUser || !privateMode"
+          :show-pin="false"
+          :item="{ icon: 'stream', label: 'nav.timelines' }"
+          :aria-expanded="showTimelines ? 'true' : 'false'"
+          @click="toggleTimelines"
+        >
+          <FAIcon
+            class="timelines-chevron"
+            fixed-width
+            :icon="showTimelines ? 'chevron-up' : 'chevron-down'"
+          />
+        </NavigationEntry>
+        <div
+          v-show="showTimelines"
+          class="timelines-background"
+        >
+          <div class="timelines">
+            <NavigationEntry
+              v-for="item in timelinesItems"
+              :key="item.name"
+              :show-pin="editMode || forceEditMode"
+              :item="item"
             />
-          </button>
-          <div
-            v-show="showTimelines"
-            class="timelines-background"
-          >
-            <ul class="timelines">
-              <NavigationEntry
-                v-for="item in timelinesItems"
-                :key="item.name"
-                :show-pin="editMode || forceEditMode"
-                :item="item"
-              />
-            </ul>
           </div>
-        </li>
-        <li v-if="currentUser">
-          <button
-            class="button-unstyled menu-item"
-            :aria-expanded="showLists ? 'true' : 'false'"
-            @click="toggleLists"
+        </div>
+        <NavigationEntry
+          v-if="currentUser"
+          :show-pin="false"
+          :item="{ icon: 'list', label: 'nav.lists' }"
+          :aria-expanded="showLists ? 'true' : 'false'"
+          @click="toggleLists"
+        >
+          <router-link
+            class="extra-button"
+            :to="{ name: 'lists' }"
+            @click.stop
           >
             <FAIcon
+              class="extra-button"
               fixed-width
-              class="fa-scale-110"
-              icon="list"
-            />{{ $t("nav.lists") }}
-            <FAIcon
-              class="timelines-chevron"
-              fixed-width
-              :icon="showLists ? 'chevron-up' : 'chevron-down'"
+              icon="wrench"
             />
-            <router-link
-              :to="{ name: 'lists' }"
-              @click.stop
-            >
-              <FAIcon
-                class="timelines-chevron"
-                fixed-width
-                icon="wrench"
-              />
-            </router-link>
-          </button>
-          <div
-            v-show="showLists"
-            class="timelines-background"
-          >
-            <ListsMenuContent
-              :show-pin="editMode || forceEditMode"
-              class="timelines"
-            />
-          </div>
-        </li>
+          </router-link>
+          <FAIcon
+            class="timelines-chevron"
+            fixed-width
+            :icon="showLists ? 'chevron-up' : 'chevron-down'"
+          />
+        </NavigationEntry>
+        <div
+          v-show="showLists"
+          class="timelines-background"
+        >
+          <ListsMenuContent
+            :show-pin="editMode || forceEditMode"
+            class="timelines"
+          />
+        </div>
         <NavigationEntry
           v-for="item in rootItems"
           :key="item.name"
           :show-pin="editMode || forceEditMode"
           :item="item"
         />
-        <li
+        <NavigationEntry
           v-if="!forceEditMode && currentUser"
-        >
-          <button
-            class="menu-item button-unstyled"
-            @click="toggleEditMode"
-          >
-            <FAIcon
-              fixed-width
-              class="fa-scale-110"
-              :icon="editMode ? 'check' : 'wrench'"
-            />
-            {{ editMode ? $t('nav.edit_finish') : $t('nav.edit_pinned') }}
-          </button>
-        </li>
+          :show-pin="false"
+          :item="{ label: editMode ? $t('nav.edit_finish') : $t('nav.edit_pinned'), icon: editMode ? 'check' : 'wrench' }"
+          @click="toggleEditMode"
+        />
       </ul>
     </div>
   </div>
@@ -161,55 +143,23 @@
     border: none;
   }
 
-  .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;
-      }
-    }
-  }
-
   .timelines-chevron {
     margin-left: 0.8em;
     margin-right: 0.8em;
     font-size: 1.1em;
   }
 
+  .menu-item {
+    .timelines-chevron {
+      margin-right: 0;
+    }
+  }
+
   .timelines-background {
     padding: 0 0 0 0.6em;
     background-color: $fallback--lightBg;
     background-color: var(--selectedMenu, $fallback--lightBg);
-    border-top: 1px solid;
+    border-bottom: 1px solid;
     border-color: $fallback--border;
     border-color: var(--border, $fallback--border);
   }
@@ -223,9 +173,5 @@
    // breaks without a unit
    --panel-heading-height-padding: 0em;
   }
-
-  .fa-scale-110 {
-    margin-right: 0.8em;
-  }
 }
 </style>
diff --git a/src/components/navigation/navigation_entry.js b/src/components/navigation/navigation_entry.js
index d8866c12..eb0a9cc4 100644
--- a/src/components/navigation/navigation_entry.js
+++ b/src/components/navigation/navigation_entry.js
@@ -22,6 +22,7 @@ const NavigationEntry = {
   },
   computed: {
     routeTo () {
+      if (!this.item.route && !this.item.routeObject) return null
       if (this.item.routeObject) {
         return this.item.routeObject
       }
diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue
index ecd4dc73..824c00a2 100644
--- a/src/components/navigation/navigation_entry.vue
+++ b/src/components/navigation/navigation_entry.vue
@@ -1,23 +1,37 @@
 <template>
   <li class="NavigationEntry">
-    <router-link
-      class="menu-item"
+    <component
+      :is="routeTo ? 'router-link' : 'button'"
+      class="menu-item button-unstyled"
       :to="routeTo"
     >
-      <FAIcon
-        v-if="item.icon"
-        fixed-width
-        class="fa-scale-110"
-        :icon="item.icon"
-      />
+      <span>
+        <FAIcon
+          v-if="item.icon"
+          fixed-width
+          class="fa-scale-110 menu-icon"
+          :icon="item.icon"
+        />
+      </span>
       <span
         v-if="item.iconLetter"
-        class="icon iconLetter fa-scale-110"
+        class="icon iconLetter fa-scale-110 menu-icon"
       >{{ item.iconLetter }}
-      </span>{{ item.labelRaw || $t(item.label) }}
+      </span>
+      <span class="label">
+      {{ item.labelRaw || $t(item.label) }}
+      </span>
+      <slot />
+      <div
+        v-if="item.badgeGetter && getters[item.badgeGetter]"
+        class="badge badge-notification"
+      >
+        {{ getters[item.badgeGetter] }}
+      </div>
       <button
+        v-if="showPin && currentUser"
         type="button"
-        class="button-unstyled"
+        class="button-unstyled extra-button"
         :title="$t(isPinned ? 'general.unpin' : 'general.pin' )"
         :aria-pressed="!!isPinned"
         @click.stop.prevent="togglePin(item.name)"
@@ -30,14 +44,8 @@
           :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>
+    </component>
   </li>
 </template>
 
@@ -47,19 +55,27 @@
 @import '../../_variables.scss';
 
 .NavigationEntry {
-  .fa-scale-110 {
+  .label {
+    flex: 1;
+  }
+
+  .menu-icon {
     margin-right: 0.8em;
   }
 
-  .badge {
-    position: absolute;
-    right: 0.6rem;
-    top: 1.25em;
+  .extra-button {
+    width: 3em;
+    text-align: center;
+
+    &:last-child {
+      margin-right: -0.8em;
+    }
   }
 
   .menu-item {
-    display: block;
+    display: flex;
     box-sizing: border-box;
+    align-items: baseline;
     height: 3.5em;
     line-height: 3.5em;
     padding: 0 1em;
@@ -75,7 +91,10 @@
       --faint: var(--selectedMenuFaintText, $fallback--faint);
       --faintLink: var(--selectedMenuFaintLink, $fallback--faint);
       --lightText: var(--selectedMenuLightText, $fallback--lightText);
-      --icon: var(--selectedMenuIcon, $fallback--icon);
+
+      .menu-icon {
+        --icon: var(--text, $fallback--icon);
+      }
     }
 
     &.router-link-active {
@@ -87,7 +106,10 @@
       --faint: var(--selectedMenuFaintText, $fallback--faint);
       --faintLink: var(--selectedMenuFaintLink, $fallback--faint);
       --lightText: var(--selectedMenuLightText, $fallback--lightText);
-      --icon: var(--selectedMenuIcon, $fallback--icon);
+
+      .menu-icon {
+        --icon: var(--text, $fallback--icon);
+      }
 
       &:hover {
         text-decoration: underline;
-- 
cgit v1.2.3-70-g09d2


From dbb6f224425e059e2edc6018d0b009cc87a0aea4 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 30 Aug 2022 02:36:41 +0300
Subject: a11y

---
 src/components/nav_panel/nav_panel.vue | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 5ee2b701..2688bcf4 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -56,6 +56,7 @@
           @click="toggleLists"
         >
           <router-link
+            :title="$t('lists.manage_lists')"
             class="extra-button"
             :to="{ name: 'lists' }"
             @click.stop
-- 
cgit v1.2.3-70-g09d2


From 8ecb67230c87754d43c4255c56c05566c8c53feb Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Wed, 31 Aug 2022 00:48:38 +0300
Subject: make custom router-link for nav-entry to un-nest the interactive
 elements

---
 src/components/nav_panel/nav_panel.vue             |   7 --
 src/components/navigation/navigation_entry.js      |   4 +
 src/components/navigation/navigation_entry.vue     | 133 +++++++++++----------
 .../optional_router_link/optional_router_link.vue  |  22 ++++
 4 files changed, 99 insertions(+), 67 deletions(-)
 create mode 100644 src/components/optional_router_link/optional_router_link.vue

(limited to 'src/components/nav_panel/nav_panel.vue')

diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 2688bcf4..7373ca63 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -121,7 +121,6 @@
     border-bottom: 1px solid;
     border-color: $fallback--border;
     border-color: var(--border, $fallback--border);
-    padding: 0;
   }
 
   > li {
@@ -150,12 +149,6 @@
     font-size: 1.1em;
   }
 
-  .menu-item {
-    .timelines-chevron {
-      margin-right: 0;
-    }
-  }
-
   .timelines-background {
     padding: 0 0 0 0.6em;
     background-color: $fallback--lightBg;
diff --git a/src/components/navigation/navigation_entry.js b/src/components/navigation/navigation_entry.js
index fe3402fc..81cc936a 100644
--- a/src/components/navigation/navigation_entry.js
+++ b/src/components/navigation/navigation_entry.js
@@ -1,5 +1,6 @@
 import { mapState } from 'vuex'
 import { USERNAME_ROUTES } from 'src/components/navigation/navigation.js'
+import OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue'
 import { library } from '@fortawesome/fontawesome-svg-core'
 import { faThumbtack } from '@fortawesome/free-solid-svg-icons'
 
@@ -7,6 +8,9 @@ library.add(faThumbtack)
 
 const NavigationEntry = {
   props: ['item', 'showPin'],
+  components: {
+    OptionalRouterLink
+  },
   methods: {
     isPinned (value) {
       return this.pinnedItems.has(value)
diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue
index 824c00a2..b984b234 100644
--- a/src/components/navigation/navigation_entry.vue
+++ b/src/components/navigation/navigation_entry.vue
@@ -1,26 +1,37 @@
 <template>
-  <li class="NavigationEntry">
-    <component
-      :is="routeTo ? 'router-link' : 'button'"
-      class="menu-item button-unstyled"
-      :to="routeTo"
+  <OptionalRouterLink
+    ass="ass"
+    :to="routeTo"
+    v-slot="{ isActive, href, navigate } = {}"
+  >
+    <li
+      class="NavigationEntry menu-item"
+      :class="{ '-active': isActive }"
+      v-bind="$attrs"
     >
-      <span>
-        <FAIcon
-          v-if="item.icon"
-          fixed-width
-          class="fa-scale-110 menu-icon"
-          :icon="item.icon"
-        />
-      </span>
-      <span
-        v-if="item.iconLetter"
-        class="icon iconLetter fa-scale-110 menu-icon"
-      >{{ item.iconLetter }}
-      </span>
-      <span class="label">
-      {{ item.labelRaw || $t(item.label) }}
-      </span>
+      <component
+        class="main-link button-unstyled"
+        :is="routeTo ? 'a' : 'button'"
+        :href="href"
+        @click="navigate"
+      >
+        <span>
+          <FAIcon
+            v-if="item.icon"
+            fixed-width
+            class="fa-scale-110 menu-icon"
+            :icon="item.icon"
+          />
+        </span>
+        <span
+          v-if="item.iconLetter"
+          class="icon iconLetter fa-scale-110 menu-icon"
+        >{{ item.iconLetter }}
+        </span>
+        <span class="label">
+        {{ item.labelRaw || $t(item.label) }}
+        </span>
+      </component>
       <slot />
       <div
         v-if="item.badgeGetter && getters[item.badgeGetter]"
@@ -45,8 +56,8 @@
           icon="thumbtack"
         />
       </button>
-    </component>
-  </li>
+    </li>
+  </OptionalRouterLink>
 </template>
 
 <script src="./navigation_entry.js"></script>
@@ -55,7 +66,21 @@
 @import '../../_variables.scss';
 
 .NavigationEntry {
-  .label {
+  display: flex;
+  box-sizing: border-box;
+  align-items: baseline;
+  height: 3.5em;
+  line-height: 3.5em;
+  padding: 0 1em;
+  width: 100%;
+  color: $fallback--link;
+  color: var(--link, $fallback--link);
+
+  .timelines-chevron {
+    margin-right: 0;
+  }
+
+  .main-link {
     flex: 1;
   }
 
@@ -72,48 +97,36 @@
     }
   }
 
-  .menu-item {
-    display: flex;
-    box-sizing: border-box;
-    align-items: baseline;
-    height: 3.5em;
-    line-height: 3.5em;
-    padding: 0 1em;
-    width: 100%;
+  &:hover {
+    background-color: $fallback--lightBg;
+    background-color: var(--selectedMenu, $fallback--lightBg);
     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);
+    color: var(--selectedMenuText, $fallback--link);
+    --faint: var(--selectedMenuFaintText, $fallback--faint);
+    --faintLink: var(--selectedMenuFaintLink, $fallback--faint);
+    --lightText: var(--selectedMenuLightText, $fallback--lightText);
 
-      .menu-icon {
-        --icon: var(--text, $fallback--icon);
-      }
+    .menu-icon {
+      --icon: var(--text, $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);
+  &.-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);
 
-      .menu-icon {
-        --icon: var(--text, $fallback--icon);
-      }
+    .menu-icon {
+      --icon: var(--text, $fallback--icon);
+    }
 
-      &:hover {
-        text-decoration: underline;
-      }
+    &:hover {
+      text-decoration: underline;
     }
   }
 }
diff --git a/src/components/optional_router_link/optional_router_link.vue b/src/components/optional_router_link/optional_router_link.vue
new file mode 100644
index 00000000..4eef0d6a
--- /dev/null
+++ b/src/components/optional_router_link/optional_router_link.vue
@@ -0,0 +1,22 @@
+<template>
+  <router-link
+    v-if="to"
+    :to="to"
+    custom
+    v-slot="props"
+  >
+    <slot
+      v-bind="props"
+    />
+  </router-link>
+  <slot
+    v-else
+    v-bind="{}"
+  />
+</template>
+
+<script>
+export default {
+  props: ['to']
+}
+</script>
-- 
cgit v1.2.3-70-g09d2

