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; margin: 0;
} }
.iconLetter {
display: inline-block;
text-align: center;
font-weight: 1000;
}
i[class*=icon-], i[class*=icon-],
.svg-inline--fa { .svg-inline--fa,
.iconLetter {
color: $fallback--icon; color: $fallback--icon;
color: var(--icon, $fallback--icon); color: var(--icon, $fallback--icon);
} }
@ -746,13 +753,15 @@ option {
} }
.fa-scale-110 { .fa-scale-110 {
&.svg-inline--fa { &.svg-inline--fa,
&.iconLetter {
font-size: 1.1em; font-size: 1.1em;
} }
} }
.fa-old-padding { .fa-old-padding {
&.svg-inline--fa { &.svg-inline--fa,
&.iconLetter {
padding: 0 0.3em; padding: 0 0.3em;
} }
} }

View File

@ -1,28 +1,26 @@
import { mapState } from 'vuex' import { mapState } from 'vuex'
import { library } from '@fortawesome/fontawesome-svg-core' import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
import {
faUsers,
faGlobe,
faBookmark,
faEnvelope,
faHome
} from '@fortawesome/free-solid-svg-icons'
library.add( export const getListEntries = state => state.lists.allLists.map(list => ({
faUsers, name: 'list-' + list.id,
faGlobe, routeObject: { name: 'lists-timeline', params: { id: list.id } },
faBookmark, labelRaw: list.title,
faEnvelope, iconLetter: list.title[0]
faHome }))
)
const ListsMenuContent = { export const ListsMenuContent = {
props: [
'showPin'
],
created () { created () {
this.$store.dispatch('startFetchingLists') this.$store.dispatch('startFetchingLists')
}, },
components: {
NavigationEntry
},
computed: { computed: {
...mapState({ ...mapState({
lists: state => state.lists.allLists, lists: getListEntries,
currentUser: state => state.users.currentUser, currentUser: state => state.users.currentUser,
privateMode: state => state.instance.private, privateMode: state => state.instance.private,
federating: state => state.instance.federating federating: state => state.instance.federating

View File

@ -1,16 +1,6 @@
<template> <template>
<ul> <ul>
<li <NavigationEntry v-for="item in lists" :key="item.name" :show-pin="showPin" :item="item" />
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>
</template> </template>

View File

@ -1,6 +1,8 @@
import TimelineMenuContent from '../timeline_menu/timeline_menu_content.vue' import { getListEntries, ListsMenuContent } from '../lists_menu/lists_menu_content.vue'
import ListsMenuContent from '../lists_menu/lists_menu_content.vue'
import { mapState, mapGetters } from 'vuex' 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 { library } from '@fortawesome/fontawesome-svg-core'
import { import {
@ -30,67 +32,6 @@ library.add(
faStream, faStream,
faList 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 = { const NavPanel = {
created () { created () {
if (this.currentUser && this.currentUser.locked) { if (this.currentUser && this.currentUser.locked) {
@ -98,8 +39,8 @@ const NavPanel = {
} }
}, },
components: { components: {
TimelineMenuContent, ListsMenuContent,
ListsMenuContent NavigationEntry
}, },
data () { data () {
return { return {
@ -134,6 +75,7 @@ const NavPanel = {
}, },
computed: { computed: {
...mapState({ ...mapState({
lists: getListEntries,
currentUser: state => state.users.currentUser, currentUser: state => state.users.currentUser,
followRequestCount: state => state.api.followRequests.length, followRequestCount: state => state.api.followRequests.length,
privateMode: state => state.instance.private, privateMode: state => state.instance.private,
@ -143,31 +85,36 @@ const NavPanel = {
collapsed: state => state.serverSideStorage.prefsStorage.simple.collapseNav collapsed: state => state.serverSideStorage.prefsStorage.simple.collapseNav
}), }),
rootItems () { rootItems () {
return Object return filterNavigation(
Object
.entries({ ...ROOT_ITEMS }) .entries({ ...ROOT_ITEMS })
.map(([k, v]) => ({ ...v, name: k })) .map(([k, v]) => ({ ...v, name: k })),
.filter(({ criteria, anon, anonRoute }) => { {
const set = new Set(criteria || []) isFederating: this.federating,
if (!this.federating && set.has('federating')) return false isPrivate: this.private,
if (this.private && set.has('!private')) return false currentUser: this.currentUser
if (!this.currentUser && !(anon || anonRoute)) return false }
if ((!this.currentUser || !this.currentUser.locked) && set.has('lockedUser')) return false )
return true
})
}, },
pinnedList () { pinnedList () {
return Object return filterNavigation(
.entries({ ...TIMELINES, ...ROOT_ITEMS }) [
.filter(([k]) => this.pinnedItems.has(k)) ...Object
.map(([k, v]) => ({ ...v, name: k })) .entries({
.filter(({ criteria, anon, anonRoute }) => { ...TIMELINES,
const set = new Set(criteria || []) ...ROOT_ITEMS
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
}) })
.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']) ...mapGetters(['unreadChatCount'])
} }

View File

@ -5,13 +5,18 @@
<span> <span>
<span v-for="item in pinnedList" :key="item.name" class="pinned-item"> <span v-for="item in pinnedList" :key="item.name" class="pinned-item">
<router-link <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 <FAIcon
v-if="item.icon"
fixed-width fixed-width
class="fa-scale-110 fa-old-padding" class="fa-scale-110 fa-old-padding"
:icon="item.icon" :icon="item.icon"
/> />
<span
v-if="item.iconLetter"
class="iconLetter fa-scale-110 fa-old-padding"
>{{ item.iconLetter }}</span>
</router-link> </router-link>
</span> </span>
</span> </span>
@ -48,7 +53,9 @@
v-show="showTimelines" v-show="showTimelines"
class="timelines-background" 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> </div>
</li> </li>
<li v-if="currentUser"> <li v-if="currentUser">
@ -81,34 +88,10 @@
v-show="showLists" v-show="showLists"
class="timelines-background" class="timelines-background"
> >
<ListsMenuContent class="timelines" /> <ListsMenuContent :showPin="true" class="timelines" />
</div> </div>
</li> </li>
<li v-for="item in rootItems" :key="item.name"> <NavigationEntry v-for="item in rootItems" :key="item.name" :show-pin="true" :item="item" />
<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> </ul>
</div> </div>
</div> </div>
@ -220,17 +203,14 @@
margin-right: 0.8em; margin-right: 0.8em;
} }
.badge {
position: absolute;
right: 0.6rem;
top: 1.25em;
}
.pinned-item { .pinned-item {
.router-link-exact-active .svg-inline--fa { .router-link-active {
& .svg-inline--fa,
& .iconLetter {
color: $fallback--text; color: $fallback--text;
color: var(--selectedMenuText, $fallback--text); color: var(--selectedMenuText, $fallback--text);
} }
} }
} }
}
</style> </style>

View File

@ -28,6 +28,7 @@ const Timeline = {
'footerSlipgate' // reference to an element where we should put our footer 'footerSlipgate' // reference to an element where we should put our footer
], ],
data () { data () {
console.log(this.timelineName)
return { return {
paused: false, paused: false,
unfocused: false, unfocused: false,

View File

@ -1,7 +1,7 @@
<template> <template>
<div :class="['Timeline', classes.root]"> <div :class="['Timeline', classes.root]">
<div :class="classes.header"> <div :class="classes.header">
<TimelineMenu v-if="!embedded" /> <TimelineMenu v-if="!embedded" :timeline-name="timelineName"/>
<button <button
v-if="showLoadButton" v-if="showLoadButton"
class="button-default loadmore-button" class="button-default loadmore-button"

View File

@ -1,6 +1,8 @@
import Popover from '../popover/popover.vue' 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 { library } from '@fortawesome/fontawesome-svg-core'
import { TIMELINES } from 'src/components/navigation/navigation.js'
import { import {
faChevronDown faChevronDown
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
@ -22,11 +24,13 @@ export const timelineNames = () => {
const TimelineMenu = { const TimelineMenu = {
components: { components: {
Popover, Popover,
TimelineMenuContent NavigationEntry,
ListsMenuContent
}, },
data () { data () {
return { return {
isOpen: false isOpen: false,
timelinesList: Object.entries(TIMELINES).map(([k, v]) => ({ ...v, name: k }))
} }
}, },
created () { created () {
@ -34,6 +38,12 @@ const TimelineMenu = {
this.$store.dispatch('setLastTimeline', this.$route.name) this.$store.dispatch('setLastTimeline', this.$route.name)
} }
}, },
computed: {
useListsMenu () {
const route = this.$route.name
return route === 'lists-timeline'
}
},
methods: { methods: {
openMenu () { openMenu () {
// $nextTick is too fast, animation won't play back but // $nextTick is too fast, animation won't play back but

View File

@ -10,7 +10,10 @@
@close="() => isOpen = false" @close="() => isOpen = false"
> >
<template #content> <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>
<template #trigger> <template #trigger>
<span class="button-unstyled title timeline-menu-title"> <span class="button-unstyled title timeline-menu-title">
@ -138,8 +141,7 @@
background-color: $fallback--lightBg; background-color: $fallback--lightBg;
background-color: var(--selectedMenu, $fallback--lightBg); background-color: var(--selectedMenu, $fallback--lightBg);
color: $fallback--text; color: $fallback--text;
color: var(--selectedMenuText, $fallback--text); color: var(--selectedMenuText, $fallback--text); --faint: var(--selectedMenuFaintText, $fallback--faint);
--faint: var(--selectedMenuFaintText, $fallback--faint);
--faintLink: var(--selectedMenuFaintLink, $fallback--faint); --faintLink: var(--selectedMenuFaintLink, $fallback--faint);
--lightText: var(--selectedMenuLightText, $fallback--lightText); --lightText: var(--selectedMenuLightText, $fallback--lightText);
--icon: var(--selectedMenuIcon, $fallback--icon); --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>