you can now pin lists
This commit is contained in:
parent
04f8c2d29d
commit
77127e2a58
11 changed files with 103 additions and 249 deletions
15
src/App.scss
15
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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'])
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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>
|
Loading…
Reference in a new issue