small refactor + clipboard copy on anchor click in instance page (#3318)
* Refacto scrollTo top and anchors * Add on click clipboard copy and notifier to anchors about/instance page * Fix margin top anchor middle title about page * Use viewportscroller for scrolltoanchor * Add to Link copied notification on instance page Co-authored-by: kimsible <kimsible@users.noreply.github.com>
This commit is contained in:
parent
901c36d5f4
commit
f3081d6401
|
@ -23,7 +23,10 @@
|
|||
<a
|
||||
*ngIf="html.administrator || maintenanceLifetime || businessModel"
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance" fragment="administrators-and-sustainability"
|
||||
routerLink="/about/instance"
|
||||
fragment="administrators-and-sustainability"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)"
|
||||
>
|
||||
<h2 i18n class="middle-title">
|
||||
ADMINISTRATORS & SUSTAINABILITY
|
||||
|
@ -32,7 +35,12 @@
|
|||
|
||||
<div class="block administrator" *ngIf="html.administrator">
|
||||
<div class="anchor" id="administrators"></div>
|
||||
<a class="anchor-link" routerLink="/about/instance" fragment="administrators">
|
||||
<a
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance"
|
||||
fragment="administrators"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)">
|
||||
<h3 i18n class="section-title">Who we are</h3>
|
||||
</a>
|
||||
|
||||
|
@ -41,7 +49,12 @@
|
|||
|
||||
<div class="block creation-reason" *ngIf="creationReason">
|
||||
<div class="anchor" id="creation-reason"></div>
|
||||
<a class="anchor-link" routerLink="/about/instance" fragment="creation-reason">
|
||||
<a
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance"
|
||||
fragment="creation-reason"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)">
|
||||
<h3 i18n class="section-title">Why we created this instance</h3>
|
||||
</a>
|
||||
|
||||
|
@ -50,7 +63,12 @@
|
|||
|
||||
<div class="block maintenance-lifetime" *ngIf="maintenanceLifetime">
|
||||
<div class="anchor" id="maintenance-lifetime"></div>
|
||||
<a class="anchor-link" routerLink="/about/instance" fragment="maintenance-lifetime">
|
||||
<a
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance"
|
||||
fragment="maintenance-lifetime"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)">
|
||||
<h3 i18n class="section-title">How long we plan to maintain this instance</h3>
|
||||
</a>
|
||||
|
||||
|
@ -58,8 +76,13 @@
|
|||
</div>
|
||||
|
||||
<div class="block business-model" *ngIf="businessModel">
|
||||
<div class="anchor" id="buisiness-model"></div>
|
||||
<a class="anchor-link" routerLink="/about/instance" fragment="buisiness-model">
|
||||
<div class="anchor" id="business-model"></div>
|
||||
<a
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance"
|
||||
fragment="business-model"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)">
|
||||
<h3 i18n class="section-title">How we will pay for this instance</h3>
|
||||
</a>
|
||||
|
||||
|
@ -67,7 +90,13 @@
|
|||
</div>
|
||||
|
||||
<div class="anchor" id="information"></div>
|
||||
<a *ngIf="html.description" class="anchor-link" routerLink="/about/instance" fragment="information">
|
||||
<a
|
||||
*ngIf="html.description"
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance"
|
||||
fragment="information"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)">
|
||||
<h2 i18n class="middle-title">
|
||||
INFORMATION
|
||||
</h2>
|
||||
|
@ -75,7 +104,12 @@
|
|||
|
||||
<div class="block description">
|
||||
<div class="anchor" id="description"></div>
|
||||
<a class="anchor-link" routerLink="/about/instance" fragment="description">
|
||||
<a
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance"
|
||||
fragment="description"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)">
|
||||
<h3 i18n class="section-title">Description</h3>
|
||||
</a>
|
||||
|
||||
|
@ -85,8 +119,11 @@
|
|||
<div class="anchor" id="moderation"></div>
|
||||
<a
|
||||
*ngIf="html.moderationInformation || html.codeOfConduct || html.terms"
|
||||
class="anchor-link" routerLink="/about/instance" fragment="moderation"
|
||||
>
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance"
|
||||
fragment="moderation"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)">
|
||||
<h2 i18n class="middle-title">
|
||||
MODERATION
|
||||
</h2>
|
||||
|
@ -94,7 +131,12 @@
|
|||
|
||||
<div class="block moderation-information" *ngIf="html.moderationInformation">
|
||||
<div class="anchor" id="moderation-information"></div>
|
||||
<a class="anchor-link" routerLink="/about/instance" fragment="moderation-information">
|
||||
<a
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance"
|
||||
fragment="moderation-information"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)">
|
||||
<h3 i18n class="section-title">Moderation information</h3>
|
||||
</a>
|
||||
|
||||
|
@ -103,7 +145,12 @@
|
|||
|
||||
<div class="block code-of-conduct" *ngIf="html.codeOfConduct">
|
||||
<div class="anchor" id="code-of-conduct"></div>
|
||||
<a class="anchor-link" routerLink="/about/instance" fragment="code-of-conduct">
|
||||
<a
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance"
|
||||
fragment="code-of-conduct"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)">
|
||||
<h3 i18n class="section-title">Code of conduct</h3>
|
||||
</a>
|
||||
|
||||
|
@ -112,7 +159,12 @@
|
|||
|
||||
<div class="block terms">
|
||||
<div class="anchor" id="terms"></div>
|
||||
<a class="anchor-link" routerLink="/about/instance" fragment="terms">
|
||||
<a
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance"
|
||||
fragment="terms"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)">
|
||||
<h3 i18n class="section-title">Terms</h3>
|
||||
</a>
|
||||
|
||||
|
@ -120,7 +172,13 @@
|
|||
</div>
|
||||
|
||||
<div class="anchor" id="other-information"></div>
|
||||
<a *ngIf="html.hardwareInformation" class="anchor-link" routerLink="/about/instance" fragment="other-information">
|
||||
<a
|
||||
*ngIf="html.hardwareInformation"
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance"
|
||||
fragment="other-information"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)">
|
||||
<h2 i18n class="middle-title">
|
||||
OTHER INFORMATION
|
||||
</h2>
|
||||
|
@ -128,7 +186,12 @@
|
|||
|
||||
<div class="block hardware-information" *ngIf="html.hardwareInformation">
|
||||
<div class="anchor" id="hardware-information"></div>
|
||||
<a class="anchor-link" routerLink="/about/instance" fragment="hardware-information">
|
||||
<a
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance"
|
||||
fragment="hardware-information"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)">
|
||||
<h3 i18n class="section-title">Hardware information</h3>
|
||||
</a>
|
||||
|
||||
|
@ -143,7 +206,12 @@
|
|||
|
||||
<div class="col">
|
||||
<div class="anchor" id="statistics"></div>
|
||||
<a class="anchor-link" routerLink="/about/instance" fragment="statistics">
|
||||
<a
|
||||
class="anchor-link"
|
||||
routerLink="/about/instance"
|
||||
fragment="statistics"
|
||||
#anchorLink
|
||||
(click)="onClickCopyLink(anchorLink)">
|
||||
<h2 i18n class="middle-title">STATISTICS</h2>
|
||||
</a>
|
||||
<my-instance-statistics></my-instance-statistics>
|
||||
|
|
|
@ -43,12 +43,12 @@
|
|||
.middle-title {
|
||||
@include in-content-small-title;
|
||||
|
||||
margin-top: 45px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.block {
|
||||
margin-bottom: 30px;
|
||||
margin-bottom: 75px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ import { ViewportScroller } from '@angular/common'
|
|||
import { AfterViewChecked, Component, OnInit, ViewChild } from '@angular/core'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-admin-modal.component'
|
||||
import { ServerService } from '@app/core'
|
||||
import { Notifier } from '@app/core'
|
||||
import { copyToClipboard } from '../../../assets/player/utils'
|
||||
import { InstanceService } from '@app/shared/shared-instance'
|
||||
import { ServerConfig } from '@shared/models'
|
||||
import { ResolverData } from './about-instance.resolver'
|
||||
|
@ -42,6 +43,7 @@ export class AboutInstanceComponent implements OnInit, AfterViewChecked {
|
|||
constructor (
|
||||
private viewportScroller: ViewportScroller,
|
||||
private route: ActivatedRoute,
|
||||
private notifier: Notifier,
|
||||
private instanceService: InstanceService
|
||||
) {}
|
||||
|
||||
|
@ -87,4 +89,10 @@ export class AboutInstanceComponent implements OnInit, AfterViewChecked {
|
|||
openContactModal () {
|
||||
return this.contactAdminModal.show()
|
||||
}
|
||||
|
||||
onClickCopyLink (anchor: HTMLAnchorElement) {
|
||||
const link = anchor.href
|
||||
copyToClipboard(link)
|
||||
this.notifier.success(link, $localize `Link copied`)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||
// scrollToAnchor first to preserve anchor position when using history navigation
|
||||
if (e.anchor) {
|
||||
setTimeout(() => {
|
||||
document.getElementById(e.anchor).scrollIntoView({ behavior: 'smooth', inline: 'nearest' })
|
||||
this.viewportScroller.scrollToAnchor(e.anchor)
|
||||
})
|
||||
|
||||
return
|
||||
|
|
|
@ -144,8 +144,12 @@ function sortBy (obj: any[], key1: string, key2?: string) {
|
|||
})
|
||||
}
|
||||
|
||||
function scrollToTop () {
|
||||
window.scroll(0, 0)
|
||||
function scrollToTop (behavior: 'auto' | 'smooth' = 'auto') {
|
||||
window.scrollTo({
|
||||
left: 0,
|
||||
top: 0,
|
||||
behavior
|
||||
})
|
||||
}
|
||||
|
||||
function isInViewport (el: HTMLElement) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="top-menu">
|
||||
<div *ngIf="isLoggedIn" class="logged-in-block">
|
||||
<div>
|
||||
<my-avatar-notification [user]="user" (navigate)="onSameUrlRestoreScrollPosition($event)"></my-avatar-notification>
|
||||
<my-avatar-notification [user]="user" (navigate)="onActiveLinkScrollToAnchor($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>
|
||||
|
@ -22,7 +22,8 @@
|
|||
|
||||
<div class="dropdown-divider"></div>
|
||||
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/notifications">
|
||||
<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>
|
||||
|
||||
|
@ -35,7 +36,7 @@
|
|||
</a>
|
||||
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings" fragment="video-languages-subtitles"
|
||||
#settingsLanguagesSubtitles (click)="onSameUrlRestoreScrollPosition(settingsLanguagesSubtitles)">
|
||||
#settingsLanguagesSubtitles (click)="onActiveLinkScrollToAnchor(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>
|
||||
|
@ -43,7 +44,7 @@
|
|||
|
||||
<a ngbDropdownItem ngbDropdownToggle class="dropdown-item" routerLink="/my-account/settings"
|
||||
fragment="video-sensitive-content-policy" #settingsSensitiveContentPolicy
|
||||
(click)="onSameUrlRestoreScrollPosition(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>
|
||||
<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>
|
||||
|
@ -72,12 +73,12 @@
|
|||
</div>
|
||||
|
||||
<div class="logged-in-menu">
|
||||
<a routerLink="/my-account" routerLinkActive="active" #settingsLink (click)="onSameUrlRestoreScrollPosition(settingsLink)">
|
||||
<a routerLink="/my-account" routerLinkActive="active" #settingsLink (click)="onActiveLinkScrollToAnchor(settingsLink)">
|
||||
<my-global-icon iconName="user" aria-hidden="true"></my-global-icon>
|
||||
<ng-container i18n>My account</ng-container>
|
||||
</a>
|
||||
|
||||
<a routerLink="/my-library" routerLinkActive="active" #libraryLink (click)="onSameUrlRestoreScrollPosition(libraryLink)">
|
||||
<a routerLink="/my-library" routerLinkActive="active" #libraryLink (click)="onActiveLinkScrollToAnchor(libraryLink)">
|
||||
<my-global-icon iconName="channel" aria-hidden="true"></my-global-icon>
|
||||
<ng-container i18n>My library</ng-container>
|
||||
</a>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { ViewportScroller } from '@angular/common'
|
||||
import { HotkeysService } from 'angular2-hotkeys'
|
||||
import * as debug from 'debug'
|
||||
import { switchMap } from 'rxjs/operators'
|
||||
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||
import { Router } from '@angular/router'
|
||||
import { scrollToTop } from '@app/helpers'
|
||||
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'
|
||||
|
@ -39,6 +41,7 @@ export class MenuComponent implements OnInit {
|
|||
}
|
||||
|
||||
constructor (
|
||||
private viewportScroller: ViewportScroller,
|
||||
private authService: AuthService,
|
||||
private userService: UserService,
|
||||
private serverService: ServerService,
|
||||
|
@ -199,23 +202,18 @@ export class MenuComponent implements OnInit {
|
|||
return this.languages.find(lang => lang.id === localeId).label
|
||||
}
|
||||
|
||||
onSameUrlRestoreScrollPosition (link: HTMLAnchorElement) {
|
||||
onActiveLinkScrollToAnchor (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'
|
||||
})
|
||||
scrollToTop('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' })
|
||||
this.viewportScroller.scrollToAnchor(linkHash)
|
||||
}
|
||||
|
||||
if (this.screenService.isInSmallView()) {
|
||||
|
|
|
@ -3,6 +3,7 @@ import { filter } from 'rxjs/operators'
|
|||
import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'
|
||||
import { NavigationEnd, Router } from '@angular/router'
|
||||
import { MenuService, ScreenService } from '@app/core'
|
||||
import { scrollToTop } from '@app/helpers'
|
||||
import { GlobalIconName } from '@app/shared/shared-icons'
|
||||
import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
|
||||
|
@ -96,11 +97,7 @@ export class TopMenuDropdownComponent implements OnInit, OnDestroy {
|
|||
|
||||
onActiveLinkScrollToTop (link: HTMLAnchorElement) {
|
||||
if (!this.isBroadcastMessageDisplayed && this.router.url.includes(link.getAttribute('href'))) {
|
||||
window.scrollTo({
|
||||
left: 0,
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
scrollToTop('smooth')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue