ability to pin items in navigation menu, initial draft version

This commit is contained in:
Henry Jameson 2022-08-11 14:30:58 +03:00
parent 732733f115
commit 6df9913354
9 changed files with 221 additions and 146 deletions

View File

@ -756,6 +756,9 @@ option {
padding: 0 0.3em;
}
}
.veryfaint {
opacity: 0.25;
}
.login-hint {
text-align: center;

View File

@ -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'])
}
}

View File

@ -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>

View File

@ -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>

View File

@ -10,7 +10,7 @@
@close="() => isOpen = false"
>
<template #content>
<TimelineMenuContent />
<TimelineMenuContent :content="timelinesList" />
</template>
<template #trigger>
<span class="button-unstyled title timeline-menu-title">

View File

@ -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
})
}
}
}

View File

@ -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>

View File

@ -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

View File

@ -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,