]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Add ability to filter menu links
authorChocobozzz <me@florianbigard.com>
Mon, 7 Jun 2021 11:16:50 +0000 (13:16 +0200)
committerChocobozzz <me@florianbigard.com>
Mon, 7 Jun 2021 11:20:08 +0000 (13:20 +0200)
client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts
client/src/app/core/menu/menu.service.ts
client/src/app/menu/menu.component.html
client/src/app/menu/menu.component.scss
client/src/app/menu/menu.component.ts
shared/models/plugins/client/client-hook.model.ts
support/doc/plugins/guide.md

index bac1015fc4e3b37174895f2a227209f0dc104716..671e734ac80562464f2bbbcd49814408112a1df3 100644 (file)
@@ -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,
index a30766b29cc01a8cebf324db6a3c87016b54046a..60130382fdb91de4aff135b02407ce813a867762 100644 (file)
@@ -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 () {
index 099ee8f36cb1b067278cd6c21de88db0e760beb0..16c79efc1f043392e4d8cb1226b474a5c09bd89e 100644 (file)
         <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">
index 6da80191a7e1d045aee7fa3a6c6ab004c11c8775..daaed2d32865cabfdd87e94dc044b4c2b5e646a4 100644 (file)
@@ -274,8 +274,7 @@ my-actor-avatar {
   }
 }
 
-.in-my-library,
-.on-instance,
+.menu-block,
 .footer-block {
   margin-bottom: 15px;
 
index c767f19b27186f957905802063127e747fd36e80..fa104cc43676b449a4f9cf6d64ccbe6f48aaecb0 100644 (file)
@@ -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()
+  }
 }
index 620651051f8da8da49336d1650930765f019e6e5..3eedd670ba15a4fdc1e01230350146b706591837 100644 (file)
@@ -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
index db1f1863cf00df94ce0af123de9c0afc061e01cd..f4d3c4800c7e23df4cfa29b6c53433e052b6bef4 100644 (file)
@@ -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