aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-12-05 14:21:10 +0100
committerChocobozzz <me@florianbigard.com>2018-12-05 14:21:31 +0100
commitddb83e49ece4e76df1af98aeea30c1d8d133f48c (patch)
tree8ea22dad72479610c71a3b660384ad1e7eaae819 /client/src
parenta1b2f876132e2c1fa8adb27bb333b2cd859dc82b (diff)
downloadPeerTube-ddb83e49ece4e76df1af98aeea30c1d8d133f48c.tar.gz
PeerTube-ddb83e49ece4e76df1af98aeea30c1d8d133f48c.tar.zst
PeerTube-ddb83e49ece4e76df1af98aeea30c1d8d133f48c.zip
My account menu -> open entries on hover
Diffstat (limited to 'client/src')
-rw-r--r--client/src/app/+my-account/my-account.component.html37
-rw-r--r--client/src/app/+my-account/my-account.component.scss15
-rw-r--r--client/src/app/+my-account/my-account.component.ts97
-rw-r--r--client/src/app/shared/menu/top-menu-dropdown.component.html18
-rw-r--r--client/src/app/shared/menu/top-menu-dropdown.component.scss14
-rw-r--r--client/src/app/shared/menu/top-menu-dropdown.component.ts75
-rw-r--r--client/src/app/shared/shared.module.ts5
7 files changed, 168 insertions, 93 deletions
diff --git a/client/src/app/+my-account/my-account.component.html b/client/src/app/+my-account/my-account.component.html
index 41333c25a..3999252be 100644
--- a/client/src/app/+my-account/my-account.component.html
+++ b/client/src/app/+my-account/my-account.component.html
@@ -1,40 +1,5 @@
1<div class="row"> 1<div class="row">
2 <div class="sub-menu"> 2 <my-top-menu-dropdown [menuEntries]="menuEntries"></my-top-menu-dropdown>
3 <a i18n routerLink="/my-account/settings" routerLinkActive="active" class="title-page">My settings</a>
4
5 <div ngbDropdown class="my-library">
6 <span role="button" class="title-page" [ngClass]="{ active: libraryLabel !== '' }" ngbDropdownToggle>
7 <ng-container i18n>My library</ng-container>
8 <ng-container *ngIf="libraryLabel"> - {{ libraryLabel }}</ng-container>
9 </span>
10
11 <div ngbDropdownMenu>
12 <a class="dropdown-item" i18n routerLink="/my-account/video-channels">My channels</a>
13
14 <a class="dropdown-item" i18n routerLink="/my-account/videos">My videos</a>
15
16 <a class="dropdown-item" i18n routerLink="/my-account/subscriptions">My subscriptions</a>
17
18 <a class="dropdown-item" *ngIf="isVideoImportEnabled()" i18n routerLink="/my-account/video-imports">My imports</a>
19 </div>
20 </div>
21
22 <div ngbDropdown class="misc">
23 <span role="button" class="title-page" [ngClass]="{ active: miscLabel !== '' }" ngbDropdownToggle>
24 <ng-container i18n>Misc</ng-container>
25 <ng-container *ngIf="miscLabel"> - {{ miscLabel }}</ng-container>
26 </span>
27
28 <div ngbDropdownMenu>
29 <a class="dropdown-item" i18n routerLink="/my-account/blocklist/accounts">Muted accounts</a>
30
31 <a class="dropdown-item" i18n routerLink="/my-account/blocklist/servers">Muted instances</a>
32
33 <a class="dropdown-item" i18n routerLink="/my-account/ownership">Ownership changes</a>
34 </div>
35 </div>
36
37 </div>
38 3
39 <div class="margin-content"> 4 <div class="margin-content">
40 <router-outlet></router-outlet> 5 <router-outlet></router-outlet>
diff --git a/client/src/app/+my-account/my-account.component.scss b/client/src/app/+my-account/my-account.component.scss
index 6243c6dcf..4f111efdf 100644
--- a/client/src/app/+my-account/my-account.component.scss
+++ b/client/src/app/+my-account/my-account.component.scss
@@ -1,14 +1,3 @@
1.my-library, .misc { 1.row {
2 span[role=button] { 2 flex-direction: column;
3 cursor: pointer;
4 }
5
6 a {
7 display: block;
8 }
9} 3}
10
11/deep/ .dropdown-toggle::after {
12 position: relative;
13 top: 2px;
14} \ No newline at end of file
diff --git a/client/src/app/+my-account/my-account.component.ts b/client/src/app/+my-account/my-account.component.ts
index d728caf07..d9381ebfa 100644
--- a/client/src/app/+my-account/my-account.component.ts
+++ b/client/src/app/+my-account/my-account.component.ts
@@ -1,38 +1,72 @@
1import { Component, OnDestroy, OnInit } from '@angular/core' 1import { Component } from '@angular/core'
2import { ServerService } from '@app/core' 2import { ServerService } from '@app/core'
3import { NavigationStart, Router } from '@angular/router'
4import { filter } from 'rxjs/operators'
5import { I18n } from '@ngx-translate/i18n-polyfill' 3import { I18n } from '@ngx-translate/i18n-polyfill'
6import { Subscription } from 'rxjs' 4import { TopMenuDropdownParam } from '@app/shared/menu/top-menu-dropdown.component'
7 5
8@Component({ 6@Component({
9 selector: 'my-my-account', 7 selector: 'my-my-account',
10 templateUrl: './my-account.component.html', 8 templateUrl: './my-account.component.html',
11 styleUrls: [ './my-account.component.scss' ] 9 styleUrls: [ './my-account.component.scss' ]
12}) 10})
13export class MyAccountComponent implements OnInit, OnDestroy { 11export class MyAccountComponent {
14 12 menuEntries: TopMenuDropdownParam[] = []
15 libraryLabel = ''
16 miscLabel = ''
17
18 private routeSub: Subscription
19 13
20 constructor ( 14 constructor (
21 private serverService: ServerService, 15 private serverService: ServerService,
22 private router: Router,
23 private i18n: I18n 16 private i18n: I18n
24 ) {} 17 ) {
18
19 const libraryEntries: TopMenuDropdownParam = {
20 label: this.i18n('My library'),
21 children: [
22 {
23 label: this.i18n('My channels'),
24 routerLink: '/my-account/videos'
25 },
26 {
27 label: this.i18n('My videos'),
28 routerLink: '/my-account/videos'
29 },
30 {
31 label: this.i18n('My subscriptions'),
32 routerLink: '/my-account/subscriptions'
33 }
34 ]
35 }
25 36
26 ngOnInit () { 37 if (this.isVideoImportEnabled()) {
27 this.updateLabels(this.router.url) 38 libraryEntries.children.push({
39 label: 'My imports',
40 routerLink: '/my-account/video-imports'
41 })
42 }
28 43
29 this.routeSub = this.router.events 44 const miscEntries: TopMenuDropdownParam = {
30 .pipe(filter(event => event instanceof NavigationStart)) 45 label: this.i18n('Misc'),
31 .subscribe((event: NavigationStart) => this.updateLabels(event.url)) 46 children: [
32 } 47 {
48 label: this.i18n('Muted accounts'),
49 routerLink: '/my-account/blocklist/accounts'
50 },
51 {
52 label: this.i18n('Muted instances'),
53 routerLink: '/my-account/blocklist/servers'
54 },
55 {
56 label: this.i18n('Ownership changes'),
57 routerLink: '/my-account/ownership'
58 }
59 ]
60 }
33 61
34 ngOnDestroy () { 62 this.menuEntries = [
35 if (this.routeSub) this.routeSub.unsubscribe() 63 {
64 label: this.i18n('My settings'),
65 routerLink: '/my-account/settings'
66 },
67 libraryEntries,
68 miscEntries
69 ]
36 } 70 }
37 71
38 isVideoImportEnabled () { 72 isVideoImportEnabled () {
@@ -41,27 +75,4 @@ export class MyAccountComponent implements OnInit, OnDestroy {
41 return importConfig.http.enabled || importConfig.torrent.enabled 75 return importConfig.http.enabled || importConfig.torrent.enabled
42 } 76 }
43 77
44 private updateLabels (url: string) {
45 const [ path ] = url.split('?')
46
47 if (path.startsWith('/my-account/video-channels')) {
48 this.libraryLabel = this.i18n('Channels')
49 } else if (path.startsWith('/my-account/videos')) {
50 this.libraryLabel = this.i18n('Videos')
51 } else if (path.startsWith('/my-account/subscriptions')) {
52 this.libraryLabel = this.i18n('Subscriptions')
53 } else if (path.startsWith('/my-account/video-imports')) {
54 this.libraryLabel = this.i18n('Video imports')
55 } else {
56 this.libraryLabel = ''
57 }
58
59 if (path.startsWith('/my-account/blocklist/accounts')) {
60 this.miscLabel = this.i18n('Muted accounts')
61 } else if (path.startsWith('/my-account/blocklist/servers')) {
62 this.miscLabel = this.i18n('Muted instances')
63 } else {
64 this.miscLabel = ''
65 }
66 }
67} 78}
diff --git a/client/src/app/shared/menu/top-menu-dropdown.component.html b/client/src/app/shared/menu/top-menu-dropdown.component.html
new file mode 100644
index 000000000..2d6d1c4bf
--- /dev/null
+++ b/client/src/app/shared/menu/top-menu-dropdown.component.html
@@ -0,0 +1,18 @@
1<div class="sub-menu">
2 <ng-container *ngFor="let menuEntry of menuEntries">
3
4 <a *ngIf="menuEntry.routerLink" [routerLink]="menuEntry.routerLink" routerLinkActive="active" class="title-page">{{ menuEntry.label }}</a>
5
6 <div *ngIf="!menuEntry.routerLink" ngbDropdown class="parent-entry" #dropdown="ngbDropdown" (mouseleave)="closeDropdownIfHovered(dropdown)">
7 <span (mouseenter)="openDropdownOnHover(dropdown)" role="button" class="title-page" [ngClass]="{ active: !!suffixLabels[menuEntry.label] }" ngbDropdownToggle>
8 <ng-container i18n>{{ menuEntry.label }}</ng-container>
9 <ng-container *ngIf="!!suffixLabels[menuEntry.label]"> - {{ suffixLabels[menuEntry.label] }}</ng-container>
10 </span>
11
12 <div ngbDropdownMenu>
13 <a *ngFor="let menuChild of menuEntry.children" class="dropdown-item" [routerLink]="menuChild.routerLink">{{ menuChild.label }}</a>
14 </div>
15 </div>
16
17 </ng-container>
18</div>
diff --git a/client/src/app/shared/menu/top-menu-dropdown.component.scss b/client/src/app/shared/menu/top-menu-dropdown.component.scss
new file mode 100644
index 000000000..f3ef8f814
--- /dev/null
+++ b/client/src/app/shared/menu/top-menu-dropdown.component.scss
@@ -0,0 +1,14 @@
1.parent-entry {
2 span[role=button] {
3 cursor: pointer;
4 }
5
6 a {
7 display: block;
8 }
9}
10
11/deep/ .dropdown-toggle::after {
12 position: relative;
13 top: 2px;
14}
diff --git a/client/src/app/shared/menu/top-menu-dropdown.component.ts b/client/src/app/shared/menu/top-menu-dropdown.component.ts
new file mode 100644
index 000000000..272b721b2
--- /dev/null
+++ b/client/src/app/shared/menu/top-menu-dropdown.component.ts
@@ -0,0 +1,75 @@
1import { Component, Input, OnDestroy, OnInit } from '@angular/core'
2import { filter, take } from 'rxjs/operators'
3import { NavigationStart, Router } from '@angular/router'
4import { Subscription } from 'rxjs'
5import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
6import { drop } from 'lodash-es'
7
8export type TopMenuDropdownParam = {
9 label: string
10 routerLink?: string
11
12 children?: {
13 label: string
14 routerLink: string
15 }[]
16}
17
18@Component({
19 selector: 'my-top-menu-dropdown',
20 templateUrl: './top-menu-dropdown.component.html',
21 styleUrls: [ './top-menu-dropdown.component.scss' ]
22})
23export class TopMenuDropdownComponent implements OnInit, OnDestroy {
24 @Input() menuEntries: TopMenuDropdownParam[] = []
25
26 suffixLabels: { [ parentLabel: string ]: string }
27
28 private openedOnHover = false
29 private routeSub: Subscription
30
31 constructor (private router: Router) {}
32
33 ngOnInit () {
34 this.updateChildLabels(window.location.pathname)
35
36 this.routeSub = this.router.events
37 .pipe(filter(event => event instanceof NavigationStart))
38 .subscribe(() => this.updateChildLabels(window.location.pathname))
39 }
40
41 ngOnDestroy () {
42 if (this.routeSub) this.routeSub.unsubscribe()
43 }
44
45 openDropdownOnHover (dropdown: NgbDropdown) {
46 this.openedOnHover = true
47 dropdown.open()
48
49 // Menu was closed
50 dropdown.openChange
51 .pipe(take(1))
52 .subscribe(e => this.openedOnHover = false)
53 }
54
55 closeDropdownIfHovered (dropdown: NgbDropdown) {
56 if (this.openedOnHover === false) return
57
58 dropdown.close()
59 this.openedOnHover = false
60 }
61
62 private updateChildLabels (path: string) {
63 this.suffixLabels = {}
64
65 for (const entry of this.menuEntries) {
66 if (!entry.children) continue
67
68 for (const child of entry.children) {
69 if (path.startsWith(child.routerLink)) {
70 this.suffixLabels[entry.label] = child.label
71 }
72 }
73 }
74 }
75}
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index a2fa27b72..9810e9485 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -61,6 +61,7 @@ import { OverviewService } from '@app/shared/overview'
61import { UserBanModalComponent } from '@app/shared/moderation' 61import { UserBanModalComponent } from '@app/shared/moderation'
62import { UserModerationDropdownComponent } from '@app/shared/moderation/user-moderation-dropdown.component' 62import { UserModerationDropdownComponent } from '@app/shared/moderation/user-moderation-dropdown.component'
63import { BlocklistService } from '@app/shared/blocklist' 63import { BlocklistService } from '@app/shared/blocklist'
64import { TopMenuDropdownComponent } from '@app/shared/menu/top-menu-dropdown.component'
64 65
65@NgModule({ 66@NgModule({
66 imports: [ 67 imports: [
@@ -102,7 +103,8 @@ import { BlocklistService } from '@app/shared/blocklist'
102 RemoteSubscribeComponent, 103 RemoteSubscribeComponent,
103 InstanceFeaturesTableComponent, 104 InstanceFeaturesTableComponent,
104 UserBanModalComponent, 105 UserBanModalComponent,
105 UserModerationDropdownComponent 106 UserModerationDropdownComponent,
107 TopMenuDropdownComponent
106 ], 108 ],
107 109
108 exports: [ 110 exports: [
@@ -141,6 +143,7 @@ import { BlocklistService } from '@app/shared/blocklist'
141 InstanceFeaturesTableComponent, 143 InstanceFeaturesTableComponent,
142 UserBanModalComponent, 144 UserBanModalComponent,
143 UserModerationDropdownComponent, 145 UserModerationDropdownComponent,
146 TopMenuDropdownComponent,
144 147
145 NumberFormatterPipe, 148 NumberFormatterPipe,
146 ObjectLengthPipe, 149 ObjectLengthPipe,