1
0
Fork 0

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:
Kim 2020-08-17 10:13:31 +02:00 committed by GitHub
parent 28fbb88f93
commit 30d55e75ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 66 additions and 17 deletions

View File

@ -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">

View File

@ -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

View File

@ -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>

View File

@ -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)
}

View File

@ -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>

View File

@ -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 = []

View File

@ -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 }}

View File

@ -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()
}