User dropdown and notifications popover improvements (#3344)
* hove user dropdown on avatar and username * rename avatar-notification to notification component * use a link on mobile for notification component * add profile user dropdown and mobile notifications link as reusable active link * replace markAllAsRead inbox glyphicon to ok in notification popover * remove keyboard shortcuts from user dropdown on mobile * use common bell icon instead of inbox-full for notifications * remove duplicated notification in user dropdown since the bell appears on the right * adjust sensitive icon in user dropdown * align vertically user buttons popover and dropdown * adjust ellipsis on user display name and username in menu * adjust notification bell for mobile in menu * display background of user dropdown avatar and username for touchscreens * add right arrow indicator on mobile Co-authored-by: kimsible <kimsible@users.noreply.github.com> Co-authored-by: Rigel Kent <sendmemail@rigelk.eu>
This commit is contained in:
parent
75594f474a
commit
51a8397006
12 changed files with 290 additions and 108 deletions
|
@ -15,7 +15,7 @@
|
|||
|
||||
<button class="btn ml-auto" [disabled]="!hasUnreadNotifications()" (click)="markAllAsRead()">
|
||||
<ng-container *ngIf="hasUnreadNotifications()">
|
||||
<my-global-icon iconName="inbox-full" aria-hidden="true"></my-global-icon>
|
||||
<my-global-icon iconName="tick" aria-hidden="true"></my-global-icon>
|
||||
|
||||
<span i18n>Mark all as read</span>
|
||||
</ng-container>
|
||||
|
|
|
@ -11,7 +11,7 @@ import { CoreModule } from './core'
|
|||
import { EmptyComponent } from './empty.component'
|
||||
import { HeaderComponent, SearchTypeaheadComponent, SuggestionComponent } from './header'
|
||||
import { HighlightPipe } from './header/highlight.pipe'
|
||||
import { AvatarNotificationComponent, LanguageChooserComponent, MenuComponent } from './menu'
|
||||
import { NotificationComponent, LanguageChooserComponent, MenuComponent } from './menu'
|
||||
import { ConfirmComponent } from './modal/confirm.component'
|
||||
import { CustomModalComponent } from './modal/custom-modal.component'
|
||||
import { InstanceConfigWarningModalComponent } from './modal/instance-config-warning-modal.component'
|
||||
|
@ -35,7 +35,7 @@ registerLocaleData(localeOc, 'oc')
|
|||
MenuComponent,
|
||||
LanguageChooserComponent,
|
||||
QuickSettingsModalComponent,
|
||||
AvatarNotificationComponent,
|
||||
NotificationComponent,
|
||||
HeaderComponent,
|
||||
SearchTypeaheadComponent,
|
||||
SuggestionComponent,
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export * from './language-chooser.component'
|
||||
export * from './avatar-notification.component'
|
||||
export * from './notification.component'
|
||||
export * from './menu.component'
|
||||
|
|
|
@ -3,32 +3,28 @@
|
|||
<div class="top-menu">
|
||||
<div *ngIf="isLoggedIn" class="logged-in-block">
|
||||
<div>
|
||||
<my-avatar-notification [user]="user" (navigate)="onActiveLinkScrollToAnchor($event)"></my-avatar-notification>
|
||||
<div class="logged-in-more" ngbDropdown #dropdown="ngbDropdown" placement="bottom-left" [container]="dropdownContainer" (openChange)="onDropdownOpenChange($event)" autoClose="outside">
|
||||
<div ngbDropdownToggle>
|
||||
<img [src]="user.accountAvatarUrl" alt="Avatar" />
|
||||
<div class="logged-in-info">
|
||||
<div class="logged-in-display-name">{{ user.account?.displayName }}</div>
|
||||
|
||||
<div class="logged-in-info">
|
||||
<a *ngIf="user.account" [routerLink]="[ '/accounts', user.account.nameWithHost ]" class="logged-in-display-name">{{ user.account?.displayName }}</a>
|
||||
<a *ngIf="!user.account" routerLink="/my-account/settings" class="logged-in-display-name">{{ user.account?.displayName }}</a>
|
||||
<div class="logged-in-username">@{{ user.username }}</div>
|
||||
</div>
|
||||
|
||||
<div class="logged-in-username">@{{ user.username }}</div>
|
||||
</div>
|
||||
|
||||
<div class="logged-in-more" ngbDropdown [placement]="loggedInMorePlacement" container="body" autoClose="outside">
|
||||
<my-global-icon iconName="more-vertical" ngbDropdownToggle role="button"></my-global-icon>
|
||||
<div class="dropdown-toggle-indicator">
|
||||
<span class="glyphicon glyphicon-chevron-down"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ngbDropdownMenu>
|
||||
<a *ngIf="user.account" ngbDropdownItem ngbDropdownToggle class="dropdown-item" [routerLink]="[ '/accounts', user.account.nameWithHost ]">
|
||||
<a *ngIf="user.account" ngbDropdownItem ngbDropdownToggle class="dropdown-item" [routerLink]="[ '/accounts', user.account.nameWithHost ]"
|
||||
#profile (click)="onActiveLinkScrollToAnchor(profile)">
|
||||
<my-global-icon iconName="go" aria-hidden="true"></my-global-icon> <ng-container i18n>Public profile</ng-container>
|
||||
</a>
|
||||
|
||||
<div class="dropdown-divider"></div>
|
||||
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/notifications"
|
||||
#notifications (click)="onActiveLinkScrollToAnchor(notifications)">
|
||||
<my-global-icon iconName="inbox-full" aria-hidden="true"></my-global-icon> <ng-container i18n>My notifications</ng-container>
|
||||
</a>
|
||||
|
||||
<div class="dropdown-divider"></div>
|
||||
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" (click)="openLanguageChooser()">
|
||||
<my-global-icon iconName="language" aria-hidden="true"></my-global-icon>
|
||||
<span i18n>Interface:</span>
|
||||
|
@ -42,7 +38,7 @@
|
|||
<span class="ml-auto text-muted">{{ videoLanguages.join(', ') }}</span>
|
||||
</a>
|
||||
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings"
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item settings-sensitive" routerLink="/my-account/settings"
|
||||
fragment="video-sensitive-content-policy" #settingsSensitiveContentPolicy
|
||||
(click)="onActiveLinkScrollToAnchor(settingsSensitiveContentPolicy)">
|
||||
<my-global-icon class="hover-display-toggle" [ngClass]="{ 'not-displayed': user.nsfwPolicy === 'display' }" iconName="sensitive" aria-hidden="true"></my-global-icon>
|
||||
|
@ -60,7 +56,7 @@
|
|||
|
||||
<div class="dropdown-divider"></div>
|
||||
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" (click)="openHotkeysCheatSheet()">
|
||||
<a *ngIf="!isInMobileView" ngbDropdownItem ngbDropdownToggle class="dropdown-item" (click)="openHotkeysCheatSheet()">
|
||||
<my-global-icon iconName="command" aria-hidden="true"></my-global-icon>
|
||||
<ng-container i18n>Keyboard shortcuts</ng-container>
|
||||
</a>
|
||||
|
@ -71,6 +67,8 @@
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<my-notification (navigate)="onActiveLinkScrollToAnchor($event)"></my-notification>
|
||||
</div>
|
||||
|
||||
<div class="logged-in-menu">
|
||||
|
|
|
@ -88,47 +88,118 @@ menu {
|
|||
height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.logged-in-info {
|
||||
@include ellipsis;
|
||||
|
||||
flex-grow: 1;
|
||||
|
||||
.logged-in-display-name {
|
||||
font-size: 16px;
|
||||
font-weight: $font-semibold;
|
||||
color: pvar(--menuForegroundColor);
|
||||
cursor: pointer;
|
||||
|
||||
@include disable-default-a-behaviour;
|
||||
}
|
||||
|
||||
.logged-in-username {
|
||||
@include ellipsis;
|
||||
|
||||
font-size: 13px;
|
||||
color: #C6C6C6;
|
||||
max-width: 140px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
justify-content: left;
|
||||
|
||||
.logged-in-more {
|
||||
margin-right: 20px;
|
||||
$main-radius: 25px;
|
||||
|
||||
my-global-icon.dropdown-toggle {
|
||||
cursor: pointer;
|
||||
margin-left: 13px;
|
||||
border-radius: $main-radius;
|
||||
transition: all .1s ease-in-out;
|
||||
cursor: pointer;
|
||||
|
||||
*, & {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&.show {
|
||||
background-color: rgba(255, 255, 255, 0.20);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .325);
|
||||
}
|
||||
|
||||
@mixin display-hints($is-mobile: false) {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
|
||||
@if $is-mobile {
|
||||
.dropdown-toggle-indicator {
|
||||
display: inherit !important;
|
||||
}
|
||||
.dropdown-toggle:first-child {
|
||||
padding-right: 30px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@include display-hints;
|
||||
}
|
||||
|
||||
/* smartphones and touchscreens */
|
||||
@media (hover: none) and (pointer: coarse) {
|
||||
@include display-hints($is-mobile: true);
|
||||
|
||||
/* fill space when on mobile */
|
||||
max-width: calc(100% - 80px);
|
||||
.dropdown-toggle {
|
||||
max-width: 100%;
|
||||
}
|
||||
.logged-in-info {
|
||||
max-width: calc(100% - 45px) !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.dropdown-toggle-indicator {
|
||||
position: relative;
|
||||
width: 0;
|
||||
display: none;
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
right: -35px;
|
||||
top: -8px;
|
||||
color: grey;
|
||||
width: $main-radius;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep {
|
||||
@include apply-svg-color(pvar(--menuForegroundColor));
|
||||
.dropdown-toggle:first-child {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 5px 7px;
|
||||
}
|
||||
|
||||
img {
|
||||
@include avatar(34px);
|
||||
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.logged-in-info {
|
||||
max-width: 105px;
|
||||
|
||||
flex-grow: 1;
|
||||
|
||||
.logged-in-display-name,
|
||||
.logged-in-username {
|
||||
@include ellipsis;
|
||||
}
|
||||
|
||||
.logged-in-display-name {
|
||||
font-size: 16px;
|
||||
font-weight: $font-semibold;
|
||||
color: pvar(--menuForegroundColor);
|
||||
|
||||
@include disable-default-a-behaviour;
|
||||
}
|
||||
|
||||
.logged-in-username {
|
||||
font-size: 13px;
|
||||
color: #C6C6C6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my-notification {
|
||||
margin-left: auto;
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.logged-in-menu {
|
||||
|
@ -343,6 +414,12 @@ menu {
|
|||
my-global-icon.hover-display-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.settings-sensitive {
|
||||
my-global-icon ::ng-deep svg {
|
||||
margin-top: 2px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,4 +441,14 @@ menu {
|
|||
.top-menu, .footer {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
width: calc(100vw - 30px);
|
||||
}
|
||||
|
||||
.dropdown-item:hover, .dropdown-item:active {
|
||||
&.settings-sensitive my-global-icon ::ng-deep svg {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import { AuthService, AuthStatus, AuthUser, MenuService, RedirectService, Screen
|
|||
import { LanguageChooserComponent } from '@app/menu/language-chooser.component'
|
||||
import { QuickSettingsModalComponent } from '@app/modal/quick-settings-modal.component'
|
||||
import { ServerConfig, UserRight, VideoConstant } from '@shared/models'
|
||||
import { NgbDropdown, NgbDropdownConfig } from '@ng-bootstrap/ng-bootstrap'
|
||||
|
||||
const logger = debug('peertube:menu:MenuComponent')
|
||||
|
||||
|
@ -20,6 +21,7 @@ const logger = debug('peertube:menu:MenuComponent')
|
|||
export class MenuComponent implements OnInit {
|
||||
@ViewChild('languageChooserModal', { static: true }) languageChooserModal: LanguageChooserComponent
|
||||
@ViewChild('quickSettingsModal', { static: true }) quickSettingsModal: QuickSettingsModalComponent
|
||||
@ViewChild('dropdown') dropdown: NgbDropdown
|
||||
|
||||
user: AuthUser
|
||||
isLoggedIn: boolean
|
||||
|
@ -30,8 +32,6 @@ export class MenuComponent implements OnInit {
|
|||
videoLanguages: string[] = []
|
||||
nsfwPolicy: string
|
||||
|
||||
loggedInMorePlacement: string
|
||||
|
||||
currentInterfaceLanguage: string
|
||||
|
||||
private languages: VideoConstant<string>[] = []
|
||||
|
@ -54,8 +54,27 @@ export class MenuComponent implements OnInit {
|
|||
private hotkeysService: HotkeysService,
|
||||
private screenService: ScreenService,
|
||||
private menuService: MenuService,
|
||||
private dropdownConfig: NgbDropdownConfig,
|
||||
private router: Router
|
||||
) { }
|
||||
) {
|
||||
this.dropdownConfig.container = 'body'
|
||||
}
|
||||
|
||||
get isInMobileView () {
|
||||
return this.screenService.isInMobileView()
|
||||
}
|
||||
|
||||
get dropdownContainer () {
|
||||
if (this.isInMobileView) {
|
||||
return null
|
||||
} else {
|
||||
return this.dropdownConfig.container
|
||||
}
|
||||
}
|
||||
|
||||
get language () {
|
||||
return this.languageChooserModal.getCurrentLanguage()
|
||||
}
|
||||
|
||||
get instanceName () {
|
||||
return this.serverConfig.instance.name
|
||||
|
@ -76,10 +95,6 @@ export class MenuComponent implements OnInit {
|
|||
|
||||
this.computeAdminAccess()
|
||||
|
||||
this.loggedInMorePlacement = this.screenService.isInMobileView()
|
||||
? 'left-top auto'
|
||||
: 'right-top auto'
|
||||
|
||||
this.currentInterfaceLanguage = this.languageChooserModal.getCurrentLanguage()
|
||||
|
||||
this.authService.loginChangedSource.subscribe(
|
||||
|
@ -203,6 +218,29 @@ export class MenuComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
// Lock menu scroll when menu scroll to avoid fleeing / detached dropdown
|
||||
onMenuScrollEvent () {
|
||||
document.querySelector('menu').scrollTo(0, 0)
|
||||
}
|
||||
|
||||
onDropdownOpenChange (opened: boolean) {
|
||||
if (this.screenService.isInMobileView()) return
|
||||
|
||||
// Close dropdown when window scroll to avoid dropdown quick jump for re-position
|
||||
const onWindowScroll = () => {
|
||||
this.dropdown.close()
|
||||
window.removeEventListener('scroll', onWindowScroll)
|
||||
}
|
||||
|
||||
if (opened) {
|
||||
window.addEventListener('scroll', onWindowScroll)
|
||||
document.querySelector('menu').scrollTo(0, 0) // Reset menu scroll to easy lock
|
||||
document.querySelector('menu').addEventListener('scroll', this.onMenuScrollEvent)
|
||||
} else {
|
||||
document.querySelector('menu').removeEventListener('scroll', this.onMenuScrollEvent)
|
||||
}
|
||||
}
|
||||
|
||||
private buildUserLanguages () {
|
||||
if (!this.user) {
|
||||
this.videoLanguages = []
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
<div
|
||||
[ngbPopover]="popContent" autoClose="outside" placement="bottom-left" container="body" popoverClass="popover-notifications"
|
||||
i18n-title title="View your notifications" class="notification-avatar" #popover="ngbPopover" (hidden)="onPopoverHidden()"
|
||||
[ngbPopover]="popContent" autoClose="outside" placement="bottom" container={this} popoverClass="popover-notifications"
|
||||
i18n-title title="View your notifications" [ngClass]="{ 'notification-inbox-popover': true, 'shown': opened, 'hidden': isInMobileView }"
|
||||
#popover="ngbPopover" (shown)="onPopoverShown()" (hidden)="onPopoverHidden()"
|
||||
>
|
||||
<div *ngIf="unreadNotifications > 0" class="unread-notifications">{{ unreadNotifications }}</div>
|
||||
|
||||
<img [src]="user.accountAvatarUrl" alt="Avatar" />
|
||||
<my-global-icon iconName="bell"></my-global-icon>
|
||||
</div>
|
||||
|
||||
<div *ngIf="isInMobileView" i18n-title title="View your notifications" class="notification-inbox-link">
|
||||
<div *ngIf="unreadNotifications > 0" class="unread-notifications">{{ unreadNotifications }}</div>
|
||||
|
||||
<a routerLink="/my-account/notifications" routerLinkActive="active" #link (click)="onNavigate(link)">
|
||||
<my-global-icon iconName="bell"></my-global-icon>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ng-template #popContent>
|
||||
|
@ -15,7 +24,7 @@
|
|||
<div>
|
||||
<button
|
||||
*ngIf="unreadNotifications"
|
||||
i18n-title title="Mark all as read" class="glyphicon glyphicon-inbox mr-2"
|
||||
i18n-title title="Mark all as read" class="glyphicon glyphicon-ok mr-2"
|
||||
(click)="markAllAsRead()"
|
||||
></button>
|
||||
<a
|
||||
|
@ -36,7 +45,7 @@
|
|||
></my-user-notifications>
|
||||
|
||||
<a *ngIf="loaded" class="all-notifications" routerLink="/my-account/notifications" #notifications (click)="onNavigate(notifications)">
|
||||
<my-global-icon class="mr-1" iconName="inbox-full" aria-hidden="true"></my-global-icon>
|
||||
<my-global-icon class="mr-1" iconName="bell" aria-hidden="true"></my-global-icon>
|
||||
<span i18n>See all your notifications</span>
|
||||
</a>
|
||||
</div>
|
|
@ -1,17 +1,62 @@
|
|||
@import '_variables';
|
||||
@import '_mixins';
|
||||
|
||||
|
||||
.notification-inbox-popover {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.notification-inbox-link a {
|
||||
padding: 13px 10px;
|
||||
}
|
||||
|
||||
.notification-inbox-popover,
|
||||
.notification-inbox-link a {
|
||||
@include apply-svg-color(#808080);
|
||||
::ng-deep {
|
||||
svg {
|
||||
transition: color .1s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
transition: all .1s ease-in-out;
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover, &:active {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
@include apply-svg-color(#fff);
|
||||
}
|
||||
}
|
||||
|
||||
.notification-inbox-popover.shown,
|
||||
.notification-inbox-link a.active {
|
||||
@include apply-svg-color(#fff);
|
||||
|
||||
background-color: rgba(255, 255, 255, 0.28);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .325);
|
||||
}
|
||||
|
||||
.notification-inbox-popover.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
::ng-deep {
|
||||
.popover-notifications.popover {
|
||||
max-width: none;
|
||||
top: -6px !important;
|
||||
left: 7px !important;
|
||||
|
||||
.arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.popover-body {
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
font-family: $main-fonts;
|
||||
width: 400px;
|
||||
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.30);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.30);
|
||||
|
||||
.loader {
|
||||
display: flex;
|
||||
|
@ -42,19 +87,22 @@
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
background-color: rgba(0, 0, 0, 0.10);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
font-size: 16px;
|
||||
min-height: 50px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
text-transform: uppercase;
|
||||
min-height: 40px;
|
||||
|
||||
a {
|
||||
@include disable-default-a-behaviour;
|
||||
}
|
||||
|
||||
|
||||
button {
|
||||
@include peertube-button;
|
||||
|
||||
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
@ -82,25 +130,23 @@
|
|||
}
|
||||
}
|
||||
|
||||
.notification-avatar {
|
||||
.notification-inbox-popover, .notification-inbox-link {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
img,
|
||||
.unread-notifications {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
img {
|
||||
@include avatar(34px);
|
||||
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.unread-notifications {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
left: -5px;
|
||||
top: 6px;
|
||||
left: 0;
|
||||
|
||||
@media screen and (max-width: $mobile-view) {
|
||||
top: -4px;
|
||||
left: -2px;
|
||||
}
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -116,19 +162,3 @@
|
|||
height: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $mobile-view) {
|
||||
::ng-deep {
|
||||
.popover-notifications.popover {
|
||||
left: unset !important;
|
||||
|
||||
.arrow {
|
||||
left: calc(2em + 7px);
|
||||
}
|
||||
|
||||
.popover-body {
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +1,24 @@
|
|||
import { Subject, Subscription } from 'rxjs'
|
||||
import { filter } from 'rxjs/operators'
|
||||
import { Component, EventEmitter, Input, Output, OnDestroy, OnInit, ViewChild } from '@angular/core'
|
||||
import { Component, EventEmitter, Output, OnDestroy, OnInit, ViewChild } from '@angular/core'
|
||||
import { NavigationEnd, Router } from '@angular/router'
|
||||
import { Notifier, User, PeerTubeSocket } from '@app/core'
|
||||
import { Notifier, PeerTubeSocket, ScreenService } from '@app/core'
|
||||
import { UserNotificationService } from '@app/shared/shared-main'
|
||||
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
|
||||
|
||||
@Component({
|
||||
selector: 'my-avatar-notification',
|
||||
templateUrl: './avatar-notification.component.html',
|
||||
styleUrls: [ './avatar-notification.component.scss' ]
|
||||
selector: 'my-notification',
|
||||
templateUrl: './notification.component.html',
|
||||
styleUrls: [ './notification.component.scss' ]
|
||||
})
|
||||
export class AvatarNotificationComponent implements OnInit, OnDestroy {
|
||||
export class NotificationComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('popover', { static: true }) popover: NgbPopover
|
||||
|
||||
@Input() user: User
|
||||
@Output() navigate = new EventEmitter<HTMLAnchorElement>()
|
||||
|
||||
unreadNotifications = 0
|
||||
loaded = false
|
||||
opened = false
|
||||
|
||||
markAllAsReadSubject = new Subject<boolean>()
|
||||
|
||||
|
@ -27,6 +27,7 @@ export class AvatarNotificationComponent implements OnInit, OnDestroy {
|
|||
|
||||
constructor (
|
||||
private userNotificationService: UserNotificationService,
|
||||
private screenService: ScreenService,
|
||||
private peertubeSocket: PeerTubeSocket,
|
||||
private notifier: Notifier,
|
||||
private router: Router
|
||||
|
@ -54,12 +55,31 @@ export class AvatarNotificationComponent implements OnInit, OnDestroy {
|
|||
if (this.routeSub) this.routeSub.unsubscribe()
|
||||
}
|
||||
|
||||
get isInMobileView () {
|
||||
return this.screenService.isInMobileView()
|
||||
}
|
||||
|
||||
closePopover () {
|
||||
this.popover.close()
|
||||
}
|
||||
|
||||
onPopoverShown () {
|
||||
this.opened = true
|
||||
|
||||
document.querySelector('menu').scrollTo(0, 0) // Reset menu scroll to easy lock
|
||||
document.querySelector('menu').addEventListener('scroll', this.onMenuScrollEvent)
|
||||
}
|
||||
|
||||
onPopoverHidden () {
|
||||
this.loaded = false
|
||||
this.opened = false
|
||||
|
||||
document.querySelector('menu').removeEventListener('scroll', this.onMenuScrollEvent)
|
||||
}
|
||||
|
||||
// Lock menu scroll when menu scroll to avoid fleeing / detached dropdown
|
||||
onMenuScrollEvent () {
|
||||
document.querySelector('menu').scrollTo(0, 0)
|
||||
}
|
||||
|
||||
onNotificationLoaded () {
|
||||
|
@ -67,6 +87,7 @@ export class AvatarNotificationComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
onNavigate (link: HTMLAnchorElement) {
|
||||
this.closePopover()
|
||||
this.navigate.emit(link)
|
||||
}
|
||||
|
||||
|
@ -83,5 +104,4 @@ export class AvatarNotificationComponent implements OnInit, OnDestroy {
|
|||
if (data.type === 'read-all') return this.unreadNotifications = 0
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -36,7 +36,7 @@ const icons = {
|
|||
'clock': require('!!raw-loader?!../../../assets/images/feather/clock.svg').default,
|
||||
'cog': require('!!raw-loader?!../../../assets/images/feather/cog.svg').default,
|
||||
'delete': require('!!raw-loader?!../../../assets/images/feather/delete.svg').default,
|
||||
'inbox-full': require('!!raw-loader?!../../../assets/images/feather/inbox-full.svg').default,
|
||||
'bell': require('!!raw-loader?!../../../assets/images/feather/bell.svg').default,
|
||||
'sign-out': require('!!raw-loader?!../../../assets/images/feather/log-out.svg').default,
|
||||
'sign-in': require('!!raw-loader?!../../../assets/images/feather/log-in.svg').default,
|
||||
'download': require('!!raw-loader?!../../../assets/images/feather/download.svg').default,
|
||||
|
|
1
client/src/assets/images/feather/bell.svg
Normal file
1
client/src/assets/images/feather/bell.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bell"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.73 21a2 2 0 0 1-3.46 0"></path></svg>
|
After Width: | Height: | Size: 321 B |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-inbox"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12"></polyline><path d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path></svg>
|
Before Width: | Height: | Size: 405 B |
Loading…
Reference in a new issue