diff options
Diffstat (limited to 'client/src/app/menu')
-rw-r--r-- | client/src/app/menu/avatar-notification.component.html | 23 | ||||
-rw-r--r-- | client/src/app/menu/avatar-notification.component.scss | 91 | ||||
-rw-r--r-- | client/src/app/menu/avatar-notification.component.ts | 65 | ||||
-rw-r--r-- | client/src/app/menu/index.ts | 2 | ||||
-rw-r--r-- | client/src/app/menu/language-chooser.component.html | 7 | ||||
-rw-r--r-- | client/src/app/menu/language-chooser.component.scss | 7 | ||||
-rw-r--r-- | client/src/app/menu/menu.component.html | 6 | ||||
-rw-r--r-- | client/src/app/menu/menu.component.scss | 17 | ||||
-rw-r--r-- | client/src/app/menu/menu.component.ts | 2 |
9 files changed, 203 insertions, 17 deletions
diff --git a/client/src/app/menu/avatar-notification.component.html b/client/src/app/menu/avatar-notification.component.html new file mode 100644 index 000000000..4ef3f0e89 --- /dev/null +++ b/client/src/app/menu/avatar-notification.component.html | |||
@@ -0,0 +1,23 @@ | |||
1 | <div | ||
2 | [ngbPopover]="popContent" autoClose="outside" placement="bottom-left" container="body" popoverClass="popover-notifications" | ||
3 | i18n-title title="View your notifications" class="notification-avatar" #popover="ngbPopover" | ||
4 | > | ||
5 | <div *ngIf="unreadNotifications > 0" class="unread-notifications">{{ unreadNotifications }}</div> | ||
6 | |||
7 | <img [src]="user.accountAvatarUrl" alt="Avatar" /> | ||
8 | </div> | ||
9 | |||
10 | <ng-template #popContent> | ||
11 | <div class="notifications-header"> | ||
12 | <div i18n>Notifications</div> | ||
13 | |||
14 | <a | ||
15 | i18n-title title="Update your notification preferences" class="glyphicon glyphicon-cog" | ||
16 | routerLink="/my-account/settings" fragment="notifications" | ||
17 | ></a> | ||
18 | </div> | ||
19 | |||
20 | <my-user-notifications [ignoreLoadingBar]="true" [infiniteScroll]="false" itemsPerPage="10"></my-user-notifications> | ||
21 | |||
22 | <a class="all-notifications" routerLink="/my-account/notifications" i18n>See all your notifications</a> | ||
23 | </ng-template> | ||
diff --git a/client/src/app/menu/avatar-notification.component.scss b/client/src/app/menu/avatar-notification.component.scss new file mode 100644 index 000000000..e785db788 --- /dev/null +++ b/client/src/app/menu/avatar-notification.component.scss | |||
@@ -0,0 +1,91 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | /deep/ { | ||
5 | .popover-notifications.popover { | ||
6 | max-width: none; | ||
7 | |||
8 | .popover-body { | ||
9 | padding: 0; | ||
10 | font-size: 14px; | ||
11 | font-family: $main-fonts; | ||
12 | overflow-y: auto; | ||
13 | max-height: 500px; | ||
14 | width: 400px; | ||
15 | box-shadow: 0 6px 14px rgba(0, 0, 0, 0.30); | ||
16 | |||
17 | .notifications-header { | ||
18 | display: flex; | ||
19 | justify-content: space-between; | ||
20 | |||
21 | background-color: rgba(0, 0, 0, 0.10); | ||
22 | align-items: center; | ||
23 | padding: 0 10px; | ||
24 | font-size: 16px; | ||
25 | height: 50px; | ||
26 | |||
27 | a { | ||
28 | @include disable-default-a-behaviour; | ||
29 | |||
30 | color: rgba(20, 20, 20, 0.5); | ||
31 | |||
32 | &:hover { | ||
33 | color: rgba(20, 20, 20, 0.8); | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | |||
38 | .all-notifications { | ||
39 | display: flex; | ||
40 | align-items: center; | ||
41 | justify-content: center; | ||
42 | font-weight: $font-semibold; | ||
43 | color: var(--mainForegroundColor); | ||
44 | padding: 7px 0; | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | } | ||
49 | |||
50 | .notification-avatar { | ||
51 | cursor: pointer; | ||
52 | position: relative; | ||
53 | |||
54 | img, | ||
55 | .unread-notifications { | ||
56 | margin-left: 20px; | ||
57 | } | ||
58 | |||
59 | img { | ||
60 | @include avatar(34px); | ||
61 | |||
62 | margin-right: 10px; | ||
63 | } | ||
64 | |||
65 | .unread-notifications { | ||
66 | position: absolute; | ||
67 | top: -5px; | ||
68 | left: -5px; | ||
69 | |||
70 | display: flex; | ||
71 | align-items: center; | ||
72 | justify-content: center; | ||
73 | |||
74 | background-color: var(--mainColor); | ||
75 | color: var(#fff); | ||
76 | font-size: 10px; | ||
77 | font-weight: $font-semibold; | ||
78 | |||
79 | border-radius: 15px; | ||
80 | width: 15px; | ||
81 | height: 15px; | ||
82 | } | ||
83 | } | ||
84 | |||
85 | @media screen and (max-width: $mobile-view) { | ||
86 | /deep/ { | ||
87 | .popover-notifications.popover .popover-body { | ||
88 | width: 400px; | ||
89 | } | ||
90 | } | ||
91 | } | ||
diff --git a/client/src/app/menu/avatar-notification.component.ts b/client/src/app/menu/avatar-notification.component.ts new file mode 100644 index 000000000..f1af08096 --- /dev/null +++ b/client/src/app/menu/avatar-notification.component.ts | |||
@@ -0,0 +1,65 @@ | |||
1 | import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core' | ||
2 | import { User } from '../shared/users/user.model' | ||
3 | import { UserNotificationService } from '@app/shared/users/user-notification.service' | ||
4 | import { Subscription } from 'rxjs' | ||
5 | import { Notifier, UserNotificationSocket } from '@app/core' | ||
6 | import { NgbPopover } from '@ng-bootstrap/ng-bootstrap' | ||
7 | import { NavigationEnd, Router } from '@angular/router' | ||
8 | import { filter } from 'rxjs/operators' | ||
9 | |||
10 | @Component({ | ||
11 | selector: 'my-avatar-notification', | ||
12 | templateUrl: './avatar-notification.component.html', | ||
13 | styleUrls: [ './avatar-notification.component.scss' ] | ||
14 | }) | ||
15 | export class AvatarNotificationComponent implements OnInit, OnDestroy { | ||
16 | @ViewChild('popover') popover: NgbPopover | ||
17 | @Input() user: User | ||
18 | |||
19 | unreadNotifications = 0 | ||
20 | |||
21 | private notificationSub: Subscription | ||
22 | private routeSub: Subscription | ||
23 | |||
24 | constructor ( | ||
25 | private userNotificationService: UserNotificationService, | ||
26 | private userNotificationSocket: UserNotificationSocket, | ||
27 | private notifier: Notifier, | ||
28 | private router: Router | ||
29 | ) {} | ||
30 | |||
31 | ngOnInit () { | ||
32 | this.userNotificationService.countUnreadNotifications() | ||
33 | .subscribe( | ||
34 | result => { | ||
35 | this.unreadNotifications = Math.min(result, 99) // Limit number to 99 | ||
36 | this.subscribeToNotifications() | ||
37 | }, | ||
38 | |||
39 | err => this.notifier.error(err.message) | ||
40 | ) | ||
41 | |||
42 | this.routeSub = this.router.events | ||
43 | .pipe(filter(event => event instanceof NavigationEnd)) | ||
44 | .subscribe(() => this.closePopover()) | ||
45 | } | ||
46 | |||
47 | ngOnDestroy () { | ||
48 | if (this.notificationSub) this.notificationSub.unsubscribe() | ||
49 | if (this.routeSub) this.routeSub.unsubscribe() | ||
50 | } | ||
51 | |||
52 | closePopover () { | ||
53 | this.popover.close() | ||
54 | } | ||
55 | |||
56 | private subscribeToNotifications () { | ||
57 | this.notificationSub = this.userNotificationSocket.getMyNotificationsSocket() | ||
58 | .subscribe(data => { | ||
59 | if (data.type === 'new') return this.unreadNotifications++ | ||
60 | if (data.type === 'read') return this.unreadNotifications-- | ||
61 | if (data.type === 'read-all') return this.unreadNotifications = 0 | ||
62 | }) | ||
63 | } | ||
64 | |||
65 | } | ||
diff --git a/client/src/app/menu/index.ts b/client/src/app/menu/index.ts index 421271c12..39dbde750 100644 --- a/client/src/app/menu/index.ts +++ b/client/src/app/menu/index.ts | |||
@@ -1 +1,3 @@ | |||
1 | export * from './language-chooser.component' | ||
2 | export * from './avatar-notification.component' | ||
1 | export * from './menu.component' | 3 | export * from './menu.component' |
diff --git a/client/src/app/menu/language-chooser.component.html b/client/src/app/menu/language-chooser.component.html index c37bf2826..a62b33dda 100644 --- a/client/src/app/menu/language-chooser.component.html +++ b/client/src/app/menu/language-chooser.component.html | |||
@@ -1,9 +1,14 @@ | |||
1 | <ng-template #modal let-hide="close"> | 1 | <ng-template #modal let-hide="close"> |
2 | <div class="modal-header"> | 2 | <div class="modal-header"> |
3 | <h4 i18n class="modal-title">Change the language</h4> | 3 | <h4 i18n class="modal-title">Change the language</h4> |
4 | <span class="close" aria-label="Close" role="button" (click)="hide()"></span> | 4 | <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon> |
5 | </div> | 5 | </div> |
6 | 6 | ||
7 | |||
8 | <a i18n class="help-to-translate" target="_blank" rel="noreferrer noopener" href="https://github.com/Chocobozzz/PeerTube/blob/develop/support/doc/translation.md"> | ||
9 | Help to translate PeerTube! | ||
10 | </a> | ||
11 | |||
7 | <div class="modal-body"> | 12 | <div class="modal-body"> |
8 | <a *ngFor="let lang of languages" [href]="buildLanguageLink(lang)">{{ lang.label }}</a> | 13 | <a *ngFor="let lang of languages" [href]="buildLanguageLink(lang)">{{ lang.label }}</a> |
9 | </div> | 14 | </div> |
diff --git a/client/src/app/menu/language-chooser.component.scss b/client/src/app/menu/language-chooser.component.scss index 944e86f46..72deb3952 100644 --- a/client/src/app/menu/language-chooser.component.scss +++ b/client/src/app/menu/language-chooser.component.scss | |||
@@ -1,6 +1,11 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | .help-to-translate { | ||
5 | @include peertube-button-link; | ||
6 | @include orange-button; | ||
7 | } | ||
8 | |||
4 | .modal-body { | 9 | .modal-body { |
5 | text-align: center; | 10 | text-align: center; |
6 | 11 | ||
@@ -9,4 +14,4 @@ | |||
9 | font-size: 16px; | 14 | font-size: 16px; |
10 | margin: 15px; | 15 | margin: 15px; |
11 | } | 16 | } |
12 | } \ No newline at end of file | 17 | } |
diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html index e04bdf3d6..aa5bfa9c9 100644 --- a/client/src/app/menu/menu.component.html +++ b/client/src/app/menu/menu.component.html | |||
@@ -2,9 +2,7 @@ | |||
2 | <menu> | 2 | <menu> |
3 | <div class="top-menu"> | 3 | <div class="top-menu"> |
4 | <div *ngIf="isLoggedIn" class="logged-in-block"> | 4 | <div *ngIf="isLoggedIn" class="logged-in-block"> |
5 | <a routerLink="/my-account/settings"> | 5 | <my-avatar-notification [user]="user"></my-avatar-notification> |
6 | <img [src]="user.accountAvatarUrl" alt="Avatar" /> | ||
7 | </a> | ||
8 | 6 | ||
9 | <div class="logged-in-info"> | 7 | <div class="logged-in-info"> |
10 | <a routerLink="/my-account/settings" class="logged-in-username">{{ user.account?.displayName }}</a> | 8 | <a routerLink="/my-account/settings" class="logged-in-username">{{ user.account?.displayName }}</a> |
@@ -97,4 +95,4 @@ | |||
97 | </menu> | 95 | </menu> |
98 | </div> | 96 | </div> |
99 | 97 | ||
100 | <my-language-chooser #languageChooserModal></my-language-chooser> \ No newline at end of file | 98 | <my-language-chooser #languageChooserModal></my-language-chooser> |
diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss index a842765ba..f30b89413 100644 --- a/client/src/app/menu/menu.component.scss +++ b/client/src/app/menu/menu.component.scss | |||
@@ -16,7 +16,7 @@ menu { | |||
16 | height: 100%; | 16 | height: 100%; |
17 | white-space: nowrap; | 17 | white-space: nowrap; |
18 | text-overflow: ellipsis; | 18 | text-overflow: ellipsis; |
19 | overflow: hidden; | 19 | overflow: auto; |
20 | color: var(--menuForegroundColor); | 20 | color: var(--menuForegroundColor); |
21 | display: flex; | 21 | display: flex; |
22 | flex-direction: column; | 22 | flex-direction: column; |
@@ -39,13 +39,6 @@ menu { | |||
39 | justify-content: center; | 39 | justify-content: center; |
40 | margin-bottom: 35px; | 40 | margin-bottom: 35px; |
41 | 41 | ||
42 | img { | ||
43 | @include avatar(34px); | ||
44 | |||
45 | margin-left: 20px; | ||
46 | margin-right: 10px; | ||
47 | } | ||
48 | |||
49 | .logged-in-info { | 42 | .logged-in-info { |
50 | flex-grow: 1; | 43 | flex-grow: 1; |
51 | 44 | ||
@@ -131,10 +124,14 @@ menu { | |||
131 | transition: background-color .1s ease-in-out; | 124 | transition: background-color .1s ease-in-out; |
132 | @include disable-default-a-behaviour; | 125 | @include disable-default-a-behaviour; |
133 | 126 | ||
134 | &:hover, &.focus-visible { | 127 | &.active { |
135 | background-color: rgba(255, 255, 255, 0.15); | 128 | background-color: rgba(255, 255, 255, 0.15); |
136 | } | 129 | } |
137 | 130 | ||
131 | &:hover, &.focus-visible { | ||
132 | background-color: rgba(255, 255, 255, 0.10); | ||
133 | } | ||
134 | |||
138 | .icon { | 135 | .icon { |
139 | @include icon(22px); | 136 | @include icon(22px); |
140 | 137 | ||
@@ -246,7 +243,7 @@ menu { | |||
246 | } | 243 | } |
247 | } | 244 | } |
248 | 245 | ||
249 | @media screen and (max-width: 400px) { | 246 | @media screen and (max-width: $mobile-view) { |
250 | .menu-wrapper { | 247 | .menu-wrapper { |
251 | width: 100% !important; | 248 | width: 100% !important; |
252 | } | 249 | } |
diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts index 95926f5f0..371beb4a5 100644 --- a/client/src/app/menu/menu.component.ts +++ b/client/src/app/menu/menu.component.ts | |||
@@ -18,7 +18,7 @@ export class MenuComponent implements OnInit { | |||
18 | userHasAdminAccess = false | 18 | userHasAdminAccess = false |
19 | helpVisible = false | 19 | helpVisible = false |
20 | 20 | ||
21 | private routesPerRight = { | 21 | private routesPerRight: { [ role in UserRight ]?: string } = { |
22 | [UserRight.MANAGE_USERS]: '/admin/users', | 22 | [UserRight.MANAGE_USERS]: '/admin/users', |
23 | [UserRight.MANAGE_SERVER_FOLLOW]: '/admin/friends', | 23 | [UserRight.MANAGE_SERVER_FOLLOW]: '/admin/friends', |
24 | [UserRight.MANAGE_VIDEO_ABUSES]: '/admin/moderation/video-abuses', | 24 | [UserRight.MANAGE_VIDEO_ABUSES]: '/admin/moderation/video-abuses', |