you can now pin lists

This commit is contained in:
Henry Jameson 2022-08-11 21:00:27 +03:00
parent 04f8c2d29d
commit 77127e2a58
11 changed files with 103 additions and 249 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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