</div>
<div class="col">
- <div id="statistics" i18n class="middle-title">
- STATISTICS
- </div>
+ <div class="anchor" id="statistics"></div>
+ <div i18n class="middle-title">STATISTICS</div>
<my-instance-statistics></my-instance-statistics>
</div>
</div>
-import { Component, OnInit, ViewChild } from '@angular/core'
+import { Component, OnInit, ViewChild, AfterViewChecked } from '@angular/core'
import { Notifier, ServerService } from '@app/core'
import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-admin-modal.component'
import { InstanceService } from '@app/shared/instance/instance.service'
import { ServerConfig } from '@shared/models'
import { ActivatedRoute } from '@angular/router'
import { ResolverData } from './about-instance.resolver'
+import { ViewportScroller } from '@angular/common'
@Component({
selector: 'my-about-instance',
templateUrl: './about-instance.component.html',
styleUrls: [ './about-instance.component.scss' ]
})
-export class AboutInstanceComponent implements OnInit {
+export class AboutInstanceComponent implements OnInit, AfterViewChecked {
@ViewChild('contactAdminModal', { static: true }) contactAdminModal: ContactAdminModalComponent
shortDescription = ''
serverConfig: ServerConfig
constructor (
+ private viewportScroller: ViewportScroller,
private route: ActivatedRoute,
- private notifier: Notifier,
private serverService: ServerService,
private instanceService: InstanceService
) {}
this.html = await this.instanceService.buildHtml(about)
}
+ ngAfterViewChecked () {
+ if (window.location.hash) this.viewportScroller.scrollToAnchor(window.location.hash.replace('#', ''))
+ }
+
openContactModal () {
return this.contactAdminModal.show()
}
<form role="form" [formGroup]="form">
- <ngb-tabset class="root-tabset bootstrap">
+ <ngb-tabset #tabs class="root-tabset bootstrap">
- <ngb-tab i18n-title title="Instance information">
+ <ngb-tab id="instance-information" i18n-title title="Instance information">
<ng-template ngbTabContent>
<ng-container formGroupName="instance">
<div class="form-group">
<label i18n for="instanceHardwareInformation">What server/hardware does the instance run on?</label>
- <div i18n class="label-small-info">2vCore 2GB RAM/or directly the link to the server you rent etc</div>
+ <div i18n class="label-small-info">i.e. 2vCore 2GB RAM, a direct the link to the server you rent, etc.</div>
<my-markdown-textarea
name="instanceHardwareInformation" formControlName="hardwareInformation" textareaWidth="500px" textareaHeight="75px" [previewColumn]="true"
</ng-template>
</ngb-tab>
- <ngb-tab i18n-title title="Basic configuration">
+ <ngb-tab id="basic-configuration" i18n-title title="Basic configuration">
<ng-template ngbTabContent>
<div class="form-row mt-5"> <!-- appearance grid -->
<div class="form-group col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">APPEARANCE</div>
<div i18n class="inner-for-description">
- Use <a routerLink="/admin/plugins">plugins & themes</a> for more involved changes.
+ Use <a routerLink="/admin/plugins">plugins & themes</a> for more involved changes, or <a routerLink="/admin/config/edit-custom" fragment="customizations" (click)="gotoAnchor()">add slight customizations</a>.
</div>
</div>
<div class="form-row mt-4"> <!-- federation grid -->
<div class="form-group col-12 col-lg-4 col-xl-3">
<div i18n class="inner-form-title">FEDERATION</div>
+ <div i18n class="inner-form-description">
+ Manage <a routerLink="/admin/follows">relations</a> with other instances.
+ </div>
</div>
<div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
</ng-template>
</ngb-tab>
- <ngb-tab i18n-title title="Services">
+ <ngb-tab id="services" i18n-title title="Services">
<ng-template ngbTabContent>
<div class="form-row mt-5"> <!-- twitter grid -->
</ng-template>
</ngb-tab>
- <ngb-tab i18n-title title="Advanced configuration">
+ <ngb-tab id="advanced-configuration" i18n-title title="Advanced configuration">
<ng-template ngbTabContent>
<div class="form-row mt-5"> <!-- transcoding grid -->
<div class="form-row mt-4"> <!-- cache grid -->
<div class="form-group col-12 col-lg-4 col-xl-3">
+ <div class="anchor" id="customizations"></div> <!-- customizations anchor -->
<div i18n class="inner-form-title">CUSTOMIZATIONS</div>
<div i18n class="inner-form-description">
Slight modifications to your PeerTube instance for when creating a plugin or theme is overkill.
-import { Component, OnInit } from '@angular/core'
+import { Component, OnInit, AfterViewChecked, ViewChild } from '@angular/core'
import { ConfigService } from '@app/+admin/config/shared/config.service'
import { ServerService } from '@app/core/server/server.service'
import { CustomConfigValidatorsService, FormReactive, UserValidatorsService } from '@app/shared'
import { SelectItem } from 'primeng/api'
import { forkJoin } from 'rxjs'
import { ServerConfig } from '@shared/models'
+import { ViewportScroller } from '@angular/common'
+import { NgbTabset } from '@ng-bootstrap/ng-bootstrap'
@Component({
selector: 'my-edit-custom-config',
templateUrl: './edit-custom-config.component.html',
styleUrls: [ './edit-custom-config.component.scss' ]
})
-export class EditCustomConfigComponent extends FormReactive implements OnInit {
+export class EditCustomConfigComponent extends FormReactive implements OnInit, AfterViewChecked {
+ @ViewChild('tabs') private tabs: NgbTabset
+
+ initDone = false
customConfig: CustomConfig
resolutions: { id: string, label: string, description?: string }[] = []
private serverConfig: ServerConfig
constructor (
+ private viewportScroller: ViewportScroller,
protected formValidatorService: FormValidatorService,
private customConfigValidatorsService: CustomConfigValidatorsService,
private userValidatorsService: UserValidatorsService,
this.checkTranscodingFields()
}
+ ngAfterViewChecked () {
+ if (!this.initDone) {
+ this.initDone = true
+ this.gotoAnchor()
+ }
+ }
+
isTranscodingEnabled () {
return this.form.value['transcoding']['enabled'] === true
}
return this.i18n('No category')
}
+ gotoAnchor () {
+ const hashToTab = {
+ 'customizations': 'advanced-configuration'
+ }
+ const hash = window.location.hash.replace('#', '')
+
+ if (hash && Object.keys(hashToTab).includes(hash)) {
+ this.tabs.select(hashToTab[hash])
+ setTimeout(() => this.viewportScroller.scrollToAnchor(hash), 100)
+ }
+ }
+
private updateForm () {
this.form.patchValue(this.customConfig)
}
<div class="form-row mt-5"> <!-- notifications grid -->
<div class="form-group col-12 col-lg-4 col-xl-3">
+ <div class="anchor" id="notifications"></div> <!-- notifications anchor -->
<div i18n class="account-title">NOTIFICATIONS</div>
</div>
-import { Component, OnInit } from '@angular/core'
+import { Component, OnInit, AfterViewChecked } from '@angular/core'
import { Notifier } from '@app/core'
import { BytesPipe } from 'ngx-pipes'
import { AuthService } from '../../core'
import { User } from '../../shared'
import { UserService } from '../../shared/users'
import { I18n } from '@ngx-translate/i18n-polyfill'
+import { ViewportScroller } from '@angular/common'
@Component({
selector: 'my-account-settings',
templateUrl: './my-account-settings.component.html',
styleUrls: [ './my-account-settings.component.scss' ]
})
-export class MyAccountSettingsComponent implements OnInit {
+export class MyAccountSettingsComponent implements OnInit, AfterViewChecked {
user: User = null
userVideoQuota = '0'
userVideoQuotaUsedDaily = 0
constructor (
+ private viewportScroller: ViewportScroller,
private userService: UserService,
private authService: AuthService,
private notifier: Notifier,
})
}
+ ngAfterViewChecked () {
+ if (window.location.hash) this.viewportScroller.scrollToAnchor(window.location.hash.replace('#', ''))
+ }
+
onAvatarChange (formData: FormData) {
this.userService.changeAvatar(formData)
.subscribe(
}
}
+.anchor {
+ position: relative;
+ top: #{-($header-height + 20px)};
+}
+
@media screen and (max-width: #{map-get($grid-breakpoints, xxl)}) {
.main-col {
&.expanded {