Verified Commit 8beea2d3 authored by Chocobozzz's avatar Chocobozzz
Browse files

Add ability to filter menu links

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