Add ability to filter menu links
This commit is contained in:
parent
9cc4b9c61f
commit
8beea2d37d
|
@ -82,6 +82,7 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
|
|||
|
||||
buildLandingPageOptions () {
|
||||
this.defaultLandingPageOptions = this.menuService.buildCommonLinks(this.serverConfig)
|
||||
.links
|
||||
.map(o => ({
|
||||
id: o.path,
|
||||
label: o.label,
|
||||
|
|
|
@ -2,16 +2,23 @@ import { fromEvent } from 'rxjs'
|
|||
import { debounceTime } from 'rxjs/operators'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { GlobalIconName } from '@app/shared/shared-icons'
|
||||
import { sortObjectComparator } from '@shared/core-utils/miscs/miscs'
|
||||
import { HTMLServerConfig } from '@shared/models/server'
|
||||
import { ScreenService } from '../wrappers'
|
||||
|
||||
export type MenuLink = {
|
||||
icon: GlobalIconName
|
||||
|
||||
label: string
|
||||
menuLabel: string
|
||||
// Used by the left menu for example
|
||||
shortLabel: string
|
||||
|
||||
path: string
|
||||
priority: number
|
||||
}
|
||||
|
||||
export type MenuSection = {
|
||||
key: string
|
||||
title: string
|
||||
links: MenuLink[]
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
|
@ -59,51 +66,90 @@ export class MenuService {
|
|||
this.isMenuDisplayed = window.innerWidth >= 800 && !this.isMenuChangedByUser
|
||||
}
|
||||
|
||||
buildCommonLinks (config: HTMLServerConfig) {
|
||||
let entries: MenuLink[] = [
|
||||
buildLibraryLinks (userCanSeeVideosLink: boolean): MenuSection {
|
||||
let links: MenuLink[] = []
|
||||
|
||||
if (userCanSeeVideosLink) {
|
||||
links.push({
|
||||
path: '/my-library/videos',
|
||||
icon: 'videos' as GlobalIconName,
|
||||
shortLabel: $localize`Videos`,
|
||||
label: $localize`My videos`
|
||||
})
|
||||
}
|
||||
|
||||
links = links.concat([
|
||||
{
|
||||
path: '/my-library/video-playlists',
|
||||
icon: 'playlists' as GlobalIconName,
|
||||
shortLabel: $localize`Playlists`,
|
||||
label: $localize`My playlists`
|
||||
},
|
||||
{
|
||||
path: '/videos/subscriptions',
|
||||
icon: 'subscriptions' as GlobalIconName,
|
||||
shortLabel: $localize`Subscriptions`,
|
||||
label: $localize`My subscriptions`
|
||||
},
|
||||
{
|
||||
path: '/my-library/history/videos',
|
||||
icon: 'history' as GlobalIconName,
|
||||
shortLabel: $localize`History`,
|
||||
label: $localize`My history`
|
||||
}
|
||||
])
|
||||
|
||||
return {
|
||||
key: 'in-my-library',
|
||||
title: 'In my library',
|
||||
links
|
||||
}
|
||||
}
|
||||
|
||||
buildCommonLinks (config: HTMLServerConfig): MenuSection {
|
||||
let links: MenuLink[] = []
|
||||
|
||||
if (config.homepage.enabled) {
|
||||
links.push({
|
||||
icon: 'home' as 'home',
|
||||
label: $localize`Home`,
|
||||
shortLabel: $localize`Home`,
|
||||
path: '/home'
|
||||
})
|
||||
}
|
||||
|
||||
links = links.concat([
|
||||
{
|
||||
icon: 'globe' as 'globe',
|
||||
label: $localize`Discover videos`,
|
||||
menuLabel: $localize`Discover`,
|
||||
path: '/videos/overview',
|
||||
priority: 150
|
||||
shortLabel: $localize`Discover`,
|
||||
path: '/videos/overview'
|
||||
},
|
||||
{
|
||||
icon: 'trending' as 'trending',
|
||||
label: $localize`Trending videos`,
|
||||
menuLabel: $localize`Trending`,
|
||||
path: '/videos/trending',
|
||||
priority: 140
|
||||
shortLabel: $localize`Trending`,
|
||||
path: '/videos/trending'
|
||||
},
|
||||
{
|
||||
icon: 'recently-added' as 'recently-added',
|
||||
label: $localize`Recently added videos`,
|
||||
menuLabel: $localize`Recently added`,
|
||||
path: '/videos/recently-added',
|
||||
priority: 130
|
||||
shortLabel: $localize`Recently added`,
|
||||
path: '/videos/recently-added'
|
||||
},
|
||||
{
|
||||
icon: 'local' as 'local',
|
||||
label: $localize`Local videos`,
|
||||
menuLabel: $localize`Local videos`,
|
||||
path: '/videos/local',
|
||||
priority: 120
|
||||
shortLabel: $localize`Local videos`,
|
||||
path: '/videos/local'
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
if (config.homepage.enabled) {
|
||||
entries.push({
|
||||
icon: 'home' as 'home',
|
||||
label: $localize`Home`,
|
||||
menuLabel: $localize`Home`,
|
||||
path: '/home',
|
||||
priority: 160
|
||||
})
|
||||
return {
|
||||
key: 'on-instance',
|
||||
title: $localize`ON ${config.instance.name}`,
|
||||
links
|
||||
}
|
||||
|
||||
entries = entries.sort(sortObjectComparator('priority', 'desc'))
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
private handleWindowResize () {
|
||||
|
|
|
@ -95,39 +95,16 @@
|
|||
<a i18n *ngIf="isRegistrationAllowed()" routerLink="/signup" class="peertube-button-link create-account-button">Create an account</a>
|
||||
</div>
|
||||
|
||||
<div *ngIf="isLoggedIn" class="in-my-library">
|
||||
<div i18n class="block-title">IN MY LIBRARY</div>
|
||||
<ng-container *ngFor="let menuSection of menuSections" >
|
||||
<div [ngClass]="[ menuSection.key, 'menu-block' ]">
|
||||
<div i18n class="block-title">{{ menuSection.title }}</div>
|
||||
|
||||
<a *ngIf="user.canSeeVideosLink" class="menu-link" routerLink="/my-library/videos" routerLinkActive="active">
|
||||
<my-global-icon iconName="videos" aria-hidden="true"></my-global-icon>
|
||||
<ng-container i18n>Videos</ng-container>
|
||||
</a>
|
||||
|
||||
<a class="menu-link" routerLink="/my-library/video-playlists" routerLinkActive="active">
|
||||
<my-global-icon iconName="playlists" aria-hidden="true"></my-global-icon>
|
||||
<ng-container i18n>Playlists</ng-container>
|
||||
</a>
|
||||
|
||||
<a class="menu-link" routerLink="/videos/subscriptions" routerLinkActive="active">
|
||||
<my-global-icon iconName="subscriptions" aria-hidden="true"></my-global-icon>
|
||||
<ng-container i18n>Subscriptions</ng-container>
|
||||
</a>
|
||||
|
||||
<a class="menu-link" routerLink="/my-library/history/videos" routerLinkActive="active">
|
||||
<my-global-icon iconName="history" aria-hidden="true"></my-global-icon>
|
||||
<ng-container i18n>History</ng-container>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="on-instance">
|
||||
<div i18n class="block-title">ON {{instanceName}}</div>
|
||||
|
||||
<a class="menu-link" *ngFor="let commonLink of commonMenuLinks" [routerLink]="commonLink.path" routerLinkActive="active">
|
||||
<my-global-icon [iconName]="commonLink.icon" aria-hidden="true"></my-global-icon>
|
||||
<ng-container>{{ commonLink.menuLabel }}</ng-container>
|
||||
</a>
|
||||
</div>
|
||||
<a class="menu-link" *ngFor="let link of menuSection.links" [routerLink]="link.path" routerLinkActive="active">
|
||||
<my-global-icon [iconName]="link.icon" aria-hidden="true"></my-global-icon>
|
||||
<ng-container>{{ link.shortLabel }}</ng-container>
|
||||
</a>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
|
|
|
@ -274,8 +274,7 @@ my-actor-avatar {
|
|||
}
|
||||
}
|
||||
|
||||
.in-my-library,
|
||||
.on-instance,
|
||||
.menu-block,
|
||||
.footer-block {
|
||||
margin-bottom: 15px;
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ import {
|
|||
AuthService,
|
||||
AuthStatus,
|
||||
AuthUser,
|
||||
MenuLink,
|
||||
HooksService,
|
||||
MenuSection,
|
||||
MenuService,
|
||||
RedirectService,
|
||||
ScreenService,
|
||||
|
@ -45,7 +46,7 @@ export class MenuComponent implements OnInit {
|
|||
|
||||
currentInterfaceLanguage: string
|
||||
|
||||
commonMenuLinks: MenuLink[] = []
|
||||
menuSections: MenuSection[] = []
|
||||
|
||||
private languages: VideoConstant<string>[] = []
|
||||
|
||||
|
@ -71,7 +72,8 @@ export class MenuComponent implements OnInit {
|
|||
private screenService: ScreenService,
|
||||
private menuService: MenuService,
|
||||
private modalService: PeertubeModalService,
|
||||
private router: Router
|
||||
private router: Router,
|
||||
private hooks: HooksService
|
||||
) { }
|
||||
|
||||
get isInMobileView () {
|
||||
|
@ -88,46 +90,23 @@ export class MenuComponent implements OnInit {
|
|||
return this.languageChooserModal.getCurrentLanguage()
|
||||
}
|
||||
|
||||
get instanceName () {
|
||||
return this.htmlServerConfig.instance.name
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.htmlServerConfig = this.serverService.getHTMLConfig()
|
||||
this.buildMenuLinks()
|
||||
|
||||
this.isLoggedIn = this.authService.isLoggedIn()
|
||||
if (this.isLoggedIn === true) {
|
||||
this.user = this.authService.getUser()
|
||||
|
||||
this.computeNSFWPolicy()
|
||||
this.computeVideosLink()
|
||||
}
|
||||
|
||||
this.computeAdminAccess()
|
||||
|
||||
this.currentInterfaceLanguage = this.languageChooserModal.getCurrentLanguage()
|
||||
|
||||
this.updateUserState()
|
||||
this.buildMenuSections()
|
||||
|
||||
this.authService.loginChangedSource.subscribe(
|
||||
status => {
|
||||
if (status === AuthStatus.LoggedIn) {
|
||||
this.isLoggedIn = true
|
||||
this.user = this.authService.getUser()
|
||||
|
||||
this.computeAdminAccess()
|
||||
this.computeVideosLink()
|
||||
|
||||
logger('Logged in.')
|
||||
} else if (status === AuthStatus.LoggedOut) {
|
||||
this.isLoggedIn = false
|
||||
this.user = undefined
|
||||
|
||||
this.computeAdminAccess()
|
||||
|
||||
logger('Logged out.')
|
||||
} else {
|
||||
console.error('Unknown auth status: ' + status)
|
||||
}
|
||||
|
||||
this.updateUserState()
|
||||
this.buildMenuSections()
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -257,8 +236,20 @@ export class MenuComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
private buildMenuLinks () {
|
||||
this.commonMenuLinks = this.menuService.buildCommonLinks(this.htmlServerConfig)
|
||||
private async buildMenuSections () {
|
||||
const menuSections = []
|
||||
|
||||
if (this.isLoggedIn) {
|
||||
menuSections.push(
|
||||
this.menuService.buildLibraryLinks(this.user?.canSeeVideosLink)
|
||||
)
|
||||
}
|
||||
|
||||
menuSections.push(
|
||||
this.menuService.buildCommonLinks(this.htmlServerConfig)
|
||||
)
|
||||
|
||||
this.menuSections = await this.hooks.wrapObject(menuSections, 'common', 'filter:left-menu.links.create.result')
|
||||
}
|
||||
|
||||
private buildUserLanguages () {
|
||||
|
@ -268,7 +259,7 @@ export class MenuComponent implements OnInit {
|
|||
}
|
||||
|
||||
if (!this.user.videoLanguages) {
|
||||
this.videoLanguages = [$localize`any language`]
|
||||
this.videoLanguages = [ $localize`any language` ]
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -284,6 +275,8 @@ export class MenuComponent implements OnInit {
|
|||
}
|
||||
|
||||
private computeVideosLink () {
|
||||
if (!this.isLoggedIn) return
|
||||
|
||||
this.authService.userInformationLoaded
|
||||
.pipe(
|
||||
switchMap(() => this.user.computeCanSeeVideosLink(this.userService.getMyVideoQuotaUsed()))
|
||||
|
@ -313,4 +306,14 @@ export class MenuComponent implements OnInit {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
private updateUserState () {
|
||||
this.user = this.isLoggedIn
|
||||
? this.authService.getUser()
|
||||
: undefined
|
||||
|
||||
this.computeAdminAccess()
|
||||
this.computeNSFWPolicy()
|
||||
this.computeVideosLink()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,10 @@ export const clientFilterHookObject = {
|
|||
|
||||
// Filter our SVG icons content
|
||||
'filter:internal.common.svg-icons.get-content.params': true,
|
||||
'filter:internal.common.svg-icons.get-content.result': true
|
||||
'filter:internal.common.svg-icons.get-content.result': true,
|
||||
|
||||
// Filter left menu links
|
||||
'filter:left-menu.links.create.result': true
|
||||
}
|
||||
|
||||
export type ClientFilterHookName = keyof typeof clientFilterHookObject
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
- [Add custom fields to video form](#add-custom-fields-to-video-form)
|
||||
- [Register settings script](#register-settings-script)
|
||||
- [HTML placeholder elements](#html-placeholder-elements)
|
||||
- [Add/remove left menu links](#addremove-left-menu-links)
|
||||
- [Publishing](#publishing)
|
||||
- [Write a plugin/theme](#write-a-plugintheme)
|
||||
- [Clone the quickstart repository](#clone-the-quickstart-repository)
|
||||
|
@ -744,6 +745,11 @@ async function register (...) {
|
|||
|
||||
See the complete list on https://docs.joinpeertube.org/api-plugins
|
||||
|
||||
#### Add/remove left menu links
|
||||
|
||||
Left menu links can be filtered (add/remove a section or add/remove links) using the `filter:left-menu.links.create.result` client hook.
|
||||
|
||||
|
||||
### Publishing
|
||||
|
||||
PeerTube plugins and themes should be published on [NPM](https://www.npmjs.com/) so that PeerTube indexes
|
||||
|
|
Loading…
Reference in New Issue