Add restore scroll position on user-dropdown anchors links and scroll to top on active sub-menu links (#3066)
* Add restore scroll position on router same url * Remove settings top anchor * Add scrollToTop on active links fixed sub-menu * Add restore scroll position on notification avatar links * Toggle menu and close pophover when click on active dropdown menu-left link * Add onSameUrlRestoreScrollPosition on user dropdown channels link * Same behavior scrollTop and scroll to anchor everywhere Co-authored-by: kimsible <kimsible@users.noreply.github.com>
This commit is contained in:
parent
28fbb88f93
commit
30d55e75ca
|
@ -1,7 +1,5 @@
|
|||
<h1 class="sr-only" i18n>Settings</h1>
|
||||
<div class="form-row"> <!-- preview -->
|
||||
<div class="anchor" id="top"></div> <!-- top anchor -->
|
||||
|
||||
<div class="form-group col-12 col-lg-4 col-xl-3"></div>
|
||||
|
||||
<div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
|
||||
|
|
|
@ -121,7 +121,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||
// scrollToAnchor first to preserve anchor position when using history navigation
|
||||
if (e.anchor) {
|
||||
setTimeout(() => {
|
||||
this.viewportScroller.scrollToAnchor(e.anchor)
|
||||
document.getElementById(e.anchor).scrollIntoView({ behavior: 'smooth', inline: 'nearest' })
|
||||
})
|
||||
|
||||
return
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<a
|
||||
i18n-title title="Update your notification preferences" class="glyphicon glyphicon-cog"
|
||||
routerLink="/my-account/settings" fragment="notifications"
|
||||
#settingsNotifications (click)="onNavigate(settingsNotifications)"
|
||||
></a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -34,7 +35,7 @@
|
|||
[markAllAsReadSubject]="markAllAsReadSubject" (notificationsLoaded)="onNotificationLoaded()"
|
||||
></my-user-notifications>
|
||||
|
||||
<a *ngIf="loaded" class="all-notifications" routerLink="/my-account/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>
|
||||
<span i18n>See all your notifications</span>
|
||||
</a>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Subject, Subscription } from 'rxjs'
|
||||
import { filter } from 'rxjs/operators'
|
||||
import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'
|
||||
import { Component, EventEmitter, Input, Output, OnDestroy, OnInit, ViewChild } from '@angular/core'
|
||||
import { NavigationEnd, Router } from '@angular/router'
|
||||
import { Notifier, User, UserNotificationSocket } from '@app/core'
|
||||
import { UserNotificationService } from '@app/shared/shared-main'
|
||||
|
@ -15,6 +15,7 @@ export class AvatarNotificationComponent implements OnInit, OnDestroy {
|
|||
@ViewChild('popover', { static: true }) popover: NgbPopover
|
||||
|
||||
@Input() user: User
|
||||
@Output() navigate = new EventEmitter<HTMLAnchorElement>()
|
||||
|
||||
unreadNotifications = 0
|
||||
loaded = false
|
||||
|
@ -65,6 +66,10 @@ export class AvatarNotificationComponent implements OnInit, OnDestroy {
|
|||
this.loaded = true
|
||||
}
|
||||
|
||||
onNavigate (link: HTMLAnchorElement) {
|
||||
this.navigate.emit(link)
|
||||
}
|
||||
|
||||
markAllAsRead () {
|
||||
this.markAllAsReadSubject.next(true)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<menu [ngClass]="{ 'logged-in': isLoggedIn }">
|
||||
<div class="top-menu">
|
||||
<div *ngIf="isLoggedIn" class="logged-in-block">
|
||||
<my-avatar-notification [user]="user"></my-avatar-notification>
|
||||
<my-avatar-notification [user]="user" (navigate)="onSameUrlRestoreScrollPosition($event)"></my-avatar-notification>
|
||||
|
||||
<div class="logged-in-info">
|
||||
<a *ngIf="user.account" [routerLink]="[ '/accounts', user.account.nameWithHost ]" class="logged-in-display-name">{{ user.account?.displayName }}</a>
|
||||
|
@ -21,11 +21,13 @@
|
|||
|
||||
<div class="dropdown-divider"></div>
|
||||
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings" fragment="top">
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings"
|
||||
#settingsLink (click)="onSameUrlRestoreScrollPosition(settingsLink)">
|
||||
<my-global-icon iconName="user" aria-hidden="true"></my-global-icon> <ng-container i18n>Account settings</ng-container>
|
||||
</a>
|
||||
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/video-channels">
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/video-channels"
|
||||
#channelsLink (click)="onSameUrlRestoreScrollPosition(channelsLink)">
|
||||
<my-global-icon iconName="channel" aria-hidden="true"></my-global-icon> <ng-container i18n>Channels settings</ng-container>
|
||||
</a>
|
||||
|
||||
|
@ -37,13 +39,16 @@
|
|||
<span class="ml-auto text-muted">{{ language }}</span>
|
||||
</a>
|
||||
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings" fragment="video-languages-subtitles">
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings" fragment="video-languages-subtitles"
|
||||
#settingsLanguagesSubtitles (click)="onSameUrlRestoreScrollPosition(settingsLanguagesSubtitles)">
|
||||
<my-global-icon iconName="video-lang" aria-hidden="true"></my-global-icon>
|
||||
<span i18n>Videos:</span>
|
||||
<span class="ml-auto text-muted">{{ videoLanguages.join(', ') }}</span>
|
||||
</a>
|
||||
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings" fragment="video-sensitive-content-policy">
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings"
|
||||
fragment="video-sensitive-content-policy" #settingsSensitiveContentPolicy
|
||||
(click)="onSameUrlRestoreScrollPosition(settingsSensitiveContentPolicy)">
|
||||
<my-global-icon class="hover-display-toggle" [ngClass]="{ 'not-displayed': user.nsfwPolicy === 'display' }" iconName="sensitive" aria-hidden="true"></my-global-icon>
|
||||
<my-global-icon class="hover-display-toggle" [ngClass]="{ 'not-displayed': user.nsfwPolicy !== 'display' }" iconName="unsensitive" aria-hidden="true"></my-global-icon>
|
||||
<span i18n>Sensitive:</span>
|
||||
|
@ -56,7 +61,8 @@
|
|||
<input type="checkbox" [checked]="user.webTorrentEnabled"/><label class="ml-auto" for="switch">Toggle p2p</label>
|
||||
</a>
|
||||
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings" fragment="top">
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings"
|
||||
#settingsMoreLink (click)="onSameUrlRestoreScrollPosition(settingsMoreLink)">
|
||||
<my-global-icon iconName="more-horizontal" aria-hidden="true"></my-global-icon> <ng-container i18n>More account settings</ng-container>
|
||||
</a>
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ import { HotkeysService } from 'angular2-hotkeys'
|
|||
import * as debug from 'debug'
|
||||
import { switchMap } from 'rxjs/operators'
|
||||
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||
import { AuthService, AuthStatus, AuthUser, RedirectService, ScreenService, ServerService, UserService } from '@app/core'
|
||||
import { Router } from '@angular/router'
|
||||
import { AuthService, AuthStatus, AuthUser, MenuService, RedirectService, ScreenService, ServerService, UserService } from '@app/core'
|
||||
import { LanguageChooserComponent } from '@app/menu/language-chooser.component'
|
||||
import { QuickSettingsModalComponent } from '@app/modal/quick-settings-modal.component'
|
||||
import { ServerConfig, UserRight, VideoConstant } from '@shared/models'
|
||||
|
@ -43,8 +44,10 @@ export class MenuComponent implements OnInit {
|
|||
private serverService: ServerService,
|
||||
private redirectService: RedirectService,
|
||||
private hotkeysService: HotkeysService,
|
||||
private screenService: ScreenService
|
||||
) { }
|
||||
private screenService: ScreenService,
|
||||
private menuService: MenuService,
|
||||
private router: Router
|
||||
) { }
|
||||
|
||||
get isInMobileView () {
|
||||
return this.screenService.isInMobileView()
|
||||
|
@ -192,6 +195,30 @@ export class MenuComponent implements OnInit {
|
|||
return this.languages.find(lang => lang.id === localeId).label
|
||||
}
|
||||
|
||||
onSameUrlRestoreScrollPosition (link: HTMLAnchorElement) {
|
||||
const linkURL = link.getAttribute('href')
|
||||
const linkHash = link.getAttribute('fragment')
|
||||
|
||||
// On same url without fragment restore top scroll position
|
||||
if (!linkHash && this.router.url.includes(linkURL)) {
|
||||
window.scrollTo({
|
||||
left: 0,
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
|
||||
// On same url with fragment restore anchor scroll position
|
||||
if (linkHash && this.router.url === linkURL) {
|
||||
const anchor = document.getElementById(link.getAttribute('fragment'))
|
||||
anchor.scrollIntoView({ behavior: 'smooth', inline: 'nearest' })
|
||||
}
|
||||
|
||||
if (this.screenService.isInSmallView()) {
|
||||
this.menuService.toggleMenu()
|
||||
}
|
||||
}
|
||||
|
||||
private buildUserLanguages () {
|
||||
if (!this.user) {
|
||||
this.videoLanguages = []
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div class="sub-menu" [ngClass]="{ 'sub-menu-fixed': !isBroadcastMessageDisplayed, 'no-scroll': isModalOpened }">
|
||||
<ng-container *ngFor="let menuEntry of menuEntries; index as id">
|
||||
|
||||
<a *ngIf="menuEntry.routerLink && isDisplayed(menuEntry)" [routerLink]="menuEntry.routerLink" routerLinkActive="active" class="title-page title-page-settings">{{ menuEntry.label }}</a>
|
||||
<a *ngIf="menuEntry.routerLink && isDisplayed(menuEntry)" [routerLink]="menuEntry.routerLink" routerLinkActive="active" class="title-page title-page-settings" #routerLink (click)="onActiveLinkScrollToTop(routerLink)">{{ menuEntry.label }}</a>
|
||||
|
||||
<div *ngIf="!menuEntry.routerLink && isDisplayed(menuEntry)" ngbDropdown class="parent-entry"
|
||||
#dropdown="ngbDropdown" autoClose="true">
|
||||
|
@ -28,7 +28,8 @@
|
|||
<ng-container *ngFor="let menuChild of menuEntry.children">
|
||||
<a *ngIf="isDisplayed(menuChild)" class="dropdown-item"
|
||||
[ngClass]="{ icon: hasIcons }"
|
||||
[routerLink]="menuChild.routerLink">
|
||||
[routerLink]="menuChild.routerLink"
|
||||
#routerLink (click)="onActiveLinkScrollToTop(routerLink)">
|
||||
<my-global-icon *ngIf="menuChild.iconName" [iconName]="menuChild.iconName" aria-hidden="true"></my-global-icon>
|
||||
|
||||
{{ menuChild.label }}
|
||||
|
@ -46,7 +47,8 @@
|
|||
<ng-container *ngFor="let menuChild of menuEntry.children">
|
||||
<a *ngIf="isDisplayed(menuChild)"
|
||||
[ngClass]="{ icon: hasIcons }"
|
||||
[routerLink]="menuChild.routerLink" routerLinkActive="active" (click)="dismissOtherModals()">
|
||||
[routerLink]="menuChild.routerLink" routerLinkActive="active"
|
||||
#routerLink (click)="dismissOtherModals(); onActiveLinkScrollToTop(routerLink)">
|
||||
<my-global-icon *ngIf="menuChild.iconName" [iconName]="menuChild.iconName" aria-hidden="true"></my-global-icon>
|
||||
|
||||
{{ menuChild.label }}
|
||||
|
|
|
@ -94,6 +94,16 @@ export class TopMenuDropdownComponent implements OnInit, OnDestroy {
|
|||
this.isModalOpened = false
|
||||
}
|
||||
|
||||
onActiveLinkScrollToTop (link: HTMLAnchorElement) {
|
||||
if (!this.isBroadcastMessageDisplayed && this.router.url.includes(link.getAttribute('href'))) {
|
||||
window.scrollTo({
|
||||
left: 0,
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
dismissOtherModals () {
|
||||
this.modalService.dismissAll()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue