<form role="form" [formGroup]="form">
- <ngb-tabset #tabs class="root-tabset bootstrap">
+ <div ngbNav #nav="ngbNav" class="nav-tabs">
- <ngb-tab id="instance-information" i18n-title title="Instance information">
- <ng-template ngbTabContent>
+ <ng-container ngbNavItem="instance-information">
+ <a ngbNavLink i18n>Instance information</a>
+
+ <ng-template ngbNavContent>
<ng-container formGroupName="instance">
</ng-container>
</ng-template>
- </ngb-tab>
+ </ng-container>
+
+ <ng-container ngbNavItem="basic-configuration">
+ <a ngbNavLink i18n>Basic configuration</a>
- <ngb-tab id="basic-configuration" i18n-title title="Basic configuration">
- <ng-template ngbTabContent>
+ <ng-template ngbNavContent>
<div class="form-row mt-5"> <!-- appearance grid -->
<div class="form-group col-12 col-lg-4 col-xl-3">
<ng-container formGroupName="import">
<ng-container formGroupName="videos">
-
+
<div class="form-group" formGroupName="http">
<my-peertube-checkbox
inputName="importVideosHttpEnabled" formControlName="enabled"
<ng-container formGroupName="autoBlacklist">
<ng-container formGroupName="videos">
<ng-container formGroupName="ofUsers">
-
+
<div class="form-group">
<my-peertube-checkbox
inputName="autoBlacklistVideosOfUsersEnabled" formControlName="enabled"
</ng-container>
</my-peertube-checkbox>
</div>
-
+
</ng-container>
</ng-container>
</ng-container>
</div>
</ng-template>
- </ngb-tab>
+ </ng-container>
+
+ <ng-container ngbNavItem="services">
+ <a ngbNavLink i18n>Services</a>
- <ngb-tab id="services" i18n-title title="Services">
- <ng-template ngbTabContent>
+ <ng-template ngbNavContent>
<div class="form-row mt-5"> <!-- twitter grid -->
<div class="form-group col-12 col-lg-4 col-xl-3">
</div>
</div>
+ </ng-template>
+ </ng-container>
- </ng-template>
- </ngb-tab>
+ <ng-container ngbNavItem="advanced-configuration">
+ <a ngbNavLink i18n>Advanced configuration</a>
- <ngb-tab id="advanced-configuration" i18n-title title="Advanced configuration">
- <ng-template ngbTabContent>
+ <ng-template ngbNavContent>
<div class="form-row mt-5"> <!-- transcoding grid -->
<div class="form-group col-12 col-lg-4 col-xl-3">
<ng-template ptTemplate="label">
<ng-container i18n>Transcoding enabled</ng-container>
</ng-template>
-
+
<ng-template ptTemplate="help">
<ng-container i18n>If you disable transcoding, many videos from your users will not work!</ng-container>
</ng-template>
</ng-container>
</my-peertube-checkbox>
</div>
-
+
<div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isTranscodingEnabled() }">
<my-peertube-checkbox
inputName="transcodingAllowAudioFiles" formControlName="allowAudioFiles"
<ng-template ptTemplate="help">
<ng-container i18n>
<strong>Experimental, we suggest you to not disable webtorrent support for now</strong>
-
+
<p>If you also enabled HLS support, it will multiply videos storage by 2</p>
<br />
<ng-template ptTemplate="help">
<ng-container i18n>
<strong>Requires ffmpeg >= 4.1</strong>
-
+
<p>Generate HLS playlists and fragmented MP4 files resulting in a better playback than with the current default player:</p>
<ul>
<li>Resolution change is smoother</li>
<li>Faster playback in particular with long videos</li>
<li>More stable playback (less bugs/infinite loading)</li>
</ul>
-
+
<p>If you also enabled WebTorrent support, it will multiply videos storage by 2</p>
</ng-container>
</ng-template>
</div>
</ng-template>
- </ngb-tab>
- </ngb-tabset>
+ </ng-container>
+ </div>
+
+ <div [ngbNavOutlet]="nav"></div>
<div class="form-row mt-4"> <!-- submit placement block -->
<div class="col-md-7 col-xl-5"></div>
-import { Component, OnInit, AfterViewChecked, ViewChild } from '@angular/core'
+import { AfterViewChecked, Component, OnInit, 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 { forkJoin } from 'rxjs'
import { ServerConfig } from '@shared/models'
import { ViewportScroller } from '@angular/common'
-import { NgbTabset } from '@ng-bootstrap/ng-bootstrap'
+import { NgbNav } from '@ng-bootstrap/ng-bootstrap'
@Component({
selector: 'my-edit-custom-config',
styleUrls: [ './edit-custom-config.component.scss' ]
})
export class EditCustomConfigComponent extends FormReactive implements OnInit, AfterViewChecked {
- @ViewChild('tabs') tabs: NgbTabset
+ // FIXME: use built-in router
+ @ViewChild('nav') nav: NgbNav
initDone = false
customConfig: CustomConfig
}
gotoAnchor () {
- const hashToTab = {
+ const hashToNav = {
'customizations': 'advanced-configuration'
}
const hash = window.location.hash.replace('#', '')
- if (hash && Object.keys(hashToTab).includes(hash)) {
- this.tabs.select(hashToTab[hash])
+ if (hash && Object.keys(hashToNav).includes(hash)) {
+ this.nav.select(hashToNav[hash])
setTimeout(() => this.viewportScroller.scrollToAnchor(hash), 100)
}
}
[id]="name" [name]="name">
</textarea>
- <ngb-tabset *ngIf="arePreviewsDisplayed()" class="previews" type="pills">
- <ngb-tab *ngIf="truncate !== undefined" i18n-title title="Truncated preview">
- <ng-template ngbTabContent><div [innerHTML]="truncatedPreviewHTML"></div></ng-template>
- </ngb-tab>
+ <div ngbNav #nav="ngbNav" class="nav-pills previews">
+ <ng-container ngbNavItem *ngIf="truncate !== undefined">
+ <a ngbNavLink i18n>Truncated preview</a>
- <ngb-tab i18n-title title="Complete preview">
- <ng-template ngbTabContent><div [innerHTML]="previewHTML"></div></ng-template>
- </ngb-tab>
- </ngb-tabset>
+ <ng-template ngbNavContent>
+ <div [innerHTML]="truncatedPreviewHTML"></div>
+ </ng-template>
+ </ng-container>
+
+ <ng-container ngbNavItem>
+ <a ngbNavLink i18n>Complete preview</a>
+
+ <ng-template ngbNavContent>
+ <div [innerHTML]="previewHTML"></div>
+ </ng-template>
+ </ng-container>
+ </div>
+
+ <div [ngbNavOutlet]="nav"></div>
</div>
NgbDropdownModule,
NgbModalModule,
NgbPopoverModule,
- NgbTabsetModule,
+ NgbNavModule,
NgbTooltipModule
} from '@ng-bootstrap/ng-bootstrap'
import { RemoteSubscribeComponent, SubscribeButtonComponent, UserSubscriptionService } from '@app/shared/user-subscription'
NgbDropdownModule,
NgbModalModule,
NgbPopoverModule,
- NgbTabsetModule,
+ NgbNavModule,
NgbTooltipModule,
NgbCollapseModule,
NgbDropdownModule,
NgbModalModule,
NgbPopoverModule,
- NgbTabsetModule,
+ NgbNavModule,
NgbTooltipModule,
NgbCollapseModule,
<ng-template #modal let-hide="close">
<div class="modal-header">
- <h4 class="modal-title">Download
- <span *ngIf="!videoCaptions" i18n>video</span>
+ <h4 class="modal-title">
+ <ng-container i18n>Download</ng-container>
<div *ngIf="videoCaptions" ngbDropdown class="d-inline-block">
<span id="dropdownDownloadType" ngbDropdownToggle>
</div>
</div>
- <ngb-tabset *ngIf="type === 'video' && videoFile?.metadata">
- <ngb-tab>
- <ng-template ngbTabTitle i18n>Format</ng-template>
- <ng-template ngbTabContent>
- <div class="file-metadata">
- <div class="metadata-attribute metadata-attribute-tags" *ngFor="let item of videoFileMetadataFormat | keyvalue">
- <span i18n class="metadata-attribute-label">{{ item.value.label }}</span>
- <span class="metadata-attribute-value">{{ item.value.value }}</span>
+ <ng-container *ngIf="type === 'video' && videoFile?.metadata">
+ <div ngbNav #nav="ngbNav" class="nav-tabs">
+
+ <ng-container ngbNavItem>
+ <a ngbNavLink i18n>Format</a>
+ <ng-template ngbNavContent>
+ <div class="file-metadata">
+ <div class="metadata-attribute metadata-attribute-tags" *ngFor="let item of videoFileMetadataFormat | keyvalue">
+ <span i18n class="metadata-attribute-label">{{ item.value.label }}</span>
+ <span class="metadata-attribute-value">{{ item.value.value }}</span>
+ </div>
</div>
- </div>
- </ng-template>
- </ngb-tab>
- <ngb-tab [disabled]="videoFileMetadataVideoStream === undefined">
- <ng-template ngbTabTitle i18n>Video stream</ng-template>
- <ng-template ngbTabContent>
- <div class="file-metadata">
- <div class="metadata-attribute metadata-attribute-tags" *ngFor="let item of videoFileMetadataVideoStream | keyvalue">
- <span i18n class="metadata-attribute-label">{{ item.value.label }}</span>
- <span class="metadata-attribute-value">{{ item.value.value }}</span>
+ </ng-template>
+ </ng-container>
+
+ <ng-container ngbNavItem [disabled]="videoFileMetadataVideoStream === undefined">
+ <a ngbNavLink i18n>Video stream</a>
+ <ng-template ngbNavContent>
+ <div class="file-metadata">
+ <div class="metadata-attribute metadata-attribute-tags" *ngFor="let item of videoFileMetadataVideoStream | keyvalue">
+ <span i18n class="metadata-attribute-label">{{ item.value.label }}</span>
+ <span class="metadata-attribute-value">{{ item.value.value }}</span>
+ </div>
</div>
- </div>
- </ng-template>
- </ngb-tab>
- <ngb-tab [disabled]="videoFileMetadataAudioStream === undefined">
- <ng-template ngbTabTitle i18n>Audio stream</ng-template>
- <ng-template ngbTabContent>
- <div class="file-metadata">
- <div class="metadata-attribute metadata-attribute-tags" *ngFor="let item of videoFileMetadataAudioStream | keyvalue">
- <span i18n class="metadata-attribute-label">{{ item.value.label }}</span>
- <span class="metadata-attribute-value">{{ item.value.value }}</span>
+ </ng-template>
+ </ng-container>
+
+ <ng-container ngbNavItem [disabled]="videoFileMetadataAudioStream === undefined">
+ <a ngbNavLink i18n>Audio stream</a>
+ <ng-template ngbNavContent>
+ <div class="file-metadata">
+ <div class="metadata-attribute metadata-attribute-tags" *ngFor="let item of videoFileMetadataAudioStream | keyvalue">
+ <span i18n class="metadata-attribute-label">{{ item.value.label }}</span>
+ <span class="metadata-attribute-value">{{ item.value.value }}</span>
+ </div>
</div>
- </div>
- </ng-template>
- </ngb-tab>
- </ngb-tabset>
+ </ng-template>
+ </ng-container>
+ </div>
+
+ <div [ngbNavOutlet]="nav"></div>
+ </ng-container>
<div class="download-type" *ngIf="type === 'video'">
<div class="peertube-radio-container">
<div class="video-edit" [formGroup]="form">
- <ngb-tabset class="root-tabset bootstrap">
+ <div ngbNav #nav="ngbNav" class="nav-tabs">
- <ngb-tab i18n-title title="Basic info">
- <ng-template ngbTabContent>
+ <ng-container ngbNavItem>
+ <a ngbNavLink i18n>Basic info</a>
+
+ <ng-template ngbNavContent>
<div class="row">
<div class="col-md-8">
<div class="form-group">
</div>
</div>
</ng-template>
- </ngb-tab>
+ </ng-container>
+
+ <ng-container ngbNavItem>
+ <a ngbNavLink i18n>Captions</a>
- <ngb-tab i18n-title title="Captions">
- <ng-template ngbTabContent>
+ <ng-template ngbNavContent>
<div class="captions">
<div class="captions-header">
</div>
</ng-template>
- </ngb-tab>
+ </ng-container>
+
+ <ng-container ngbNavItem>
+ <a ngbNavLink i18n>Advanced settings</a>
- <ngb-tab i18n-title title="Advanced settings">
- <ng-template ngbTabContent>
+ <ng-template ngbNavContent>
<div class="row advanced-settings">
<div class="col-md-12 col-xl-8">
</div>
</div>
</ng-template>
- </ngb-tab>
+ </ng-container>
- </ngb-tabset>
+ </div>
+ <div [ngbNavOutlet]="nav"></div>
</div>
<my-video-caption-add-modal
margin-bottom: 1rem;
}
+.nav-tabs {
+ margin-bottom: 15px;
+}
+
.video-edit {
height: 100%;
min-height: 300px;
}
::ng-deep {
- .root-tabset > .nav {
- margin-bottom: 15px;
- }
-
.ng2-tag-input {
border: none !important;
}
<ng-container *ngIf="secondStepType === 'upload'" i18n>Upload {{ videoName }}</ng-container>
</div>
- <ngb-tabset class="video-add-tabset root-tabset bootstrap" [ngClass]="{ 'hide-nav': secondStepType !== undefined }">
+ <div ngbNav #nav="ngbNav" class="nav-tabs video-add-nav" [ngClass]="{ 'hide-nav': secondStepType !== undefined }">
+ <ng-container ngbNavItem>
+ <a ngbNavLink>
+ <span i18n>Upload a file</span>
+ </a>
- <ngb-tab>
- <ng-template ngbTabTitle><span i18n>Upload a file</span></ng-template>
- <ng-template ngbTabContent>
+ <ng-template ngbNavContent>
<my-video-upload #videoUpload (firstStepDone)="onFirstStepDone('upload', $event)" (firstStepError)="onError()"></my-video-upload>
</ng-template>
- </ngb-tab>
+ </ng-container>
- <ngb-tab *ngIf="isVideoImportHttpEnabled()">
- <ng-template ngbTabTitle><span i18n>Import with URL</span></ng-template>
- <ng-template ngbTabContent>
+ <ng-container ngbNavItem *ngIf="isVideoImportHttpEnabled()">
+ <a ngbNavLink>
+ <span i18n>Import with URL</span>
+ </a>
+
+ <ng-template ngbNavContent>
<my-video-import-url #videoImportUrl (firstStepDone)="onFirstStepDone('import-url', $event)" (firstStepError)="onError()"></my-video-import-url>
</ng-template>
- </ngb-tab>
+ </ng-container>
+
+ <ng-container ngbNavItem *ngIf="isVideoImportTorrentEnabled()">
+ <a ngbNavLink>
+ <span i18n>Import with torrent</span>
+ </a>
- <ngb-tab *ngIf="isVideoImportTorrentEnabled()">
- <ng-template ngbTabTitle><span i18n>Import with torrent</span></ng-template>
- <ng-template ngbTabContent>
+ <ng-template ngbNavContent>
<my-video-import-torrent #videoImportTorrent (firstStepDone)="onFirstStepDone('import-torrent', $event)" (firstStepError)="onError()"></my-video-import-torrent>
</ng-template>
- </ngb-tab>
- </ngb-tabset>
+ </ng-container>
+ </div>
+
+ <div [ngbNavOutlet]="nav"></div>
</div>
font-size: 15px;
}
-::ng-deep .root-tabset.video-add-tabset {
- margin-top: 50px;
-
- &.hide-nav > .nav {
- display: none !important;
- }
-
- & > .nav {
- border-bottom: $border-width $border-type $border-color;
- margin: 0 !important;
-
- & > li {
- margin-bottom: -$border-width;
- }
-
- a.nav-link {
- @include disable-default-a-behaviour;
-
- height: 40px !important;
- padding: 0 30px !important;
- font-size: 15px;
-
- &.active {
- border: $border-width $border-type $border-color;
- border-bottom: none;
- background-color: var(--submenuColor) !important;
-
- span {
- border-bottom: 2px solid var(--mainColor);
- font-weight: $font-bold;
- }
+::ng-deep .video-add-nav {
+ border-bottom: $border-width $border-type $border-color;
+ margin: 50px 0 0 0 !important;
+
+ a.nav-link {
+ @include disable-default-a-behaviour;
+
+ margin-bottom: -$border-width;
+ height: 40px !important;
+ padding: 0 30px !important;
+ font-size: 15px;
+
+ &.active {
+ border: $border-width $border-type $border-color;
+ border-bottom: none;
+ background-color: var(--submenuColor) !important;
+
+ span {
+ border-bottom: 2px solid var(--mainColor);
+ font-weight: $font-bold;
}
}
}
+}
- .upload-video-container {
- border: $border-width $border-type $border-color;
- border-top: none;
-
- background-color: var(--submenuColor);
- border-radius: 3px;
- width: 100%;
- min-height: 440px;
- padding-bottom: 20px;
- display: flex;
- justify-content: center;
- align-items: center;
- }
+::ng-deep .upload-video-container {
+ border: $border-width $border-type $border-color;
+ border-top: none;
+
+ background-color: var(--submenuColor);
+ border-radius: 3px;
+ width: 100%;
+ min-height: 440px;
+ padding-bottom: 20px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
}
<div class="video">
<div class="title-page title-page-single" *ngIf="hasPlaylist()" i18n>Share the video</div>
- <ngb-tabset class="root-tabset bootstrap" (tabChange)="onTabChange($event)">
+ <div ngbNav #nav="ngbNav" class="nav-tabs" [(activeId)]="activeId">
- <ngb-tab i18n-title title="URL" id="url">
- <ng-template ngbTabContent>
+ <ng-container ngbNavItem="url">
+ <a ngbNavLink i18n>URL</a>
- <div class="tab-content">
+ <ng-template ngbNavContent>
+ <div class="nav-content">
<my-input-readonly-copy [value]="getVideoUrl()"></my-input-readonly-copy>
</div>
-
</ng-template>
- </ngb-tab>
+ </ng-container>
+
+ <ng-container ngbNavItem="qrcode">
+ <a ngbNavLink i18n>QR-Code</a>
- <ngb-tab i18n-title title="QR-Code" id="qrcode">
- <ng-template ngbTabContent>
- <div class="tab-content">
+ <ng-template ngbNavContent>
+ <div class="nav-content">
<qrcode [qrdata]="getVideoUrl()" [size]="256" level="Q"></qrcode>
</div>
</ng-template>
- </ngb-tab>
+ </ng-container>
+
+ <ng-container ngbNavItem="embed">
+ <a ngbNavLink i18n>Embed</a>
- <ngb-tab i18n-title title="Embed" id="embed">
- <ng-template ngbTabContent>
- <div class="tab-content">
+ <ng-template ngbNavContent>
+ <div class="nav-content">
<my-input-readonly-copy [value]="getVideoIframeCode()"></my-input-readonly-copy>
<div i18n *ngIf="notSecure()" class="alert alert-warning">
</div>
</div>
</ng-template>
- </ngb-tab>
+ </ng-container>
+
+ </div>
- </ngb-tabset>
+ <div [ngbNavOutlet]="nav"></div>
<div class="filters">
<div>
text-align: center;
}
-.tab-content {
+.nav-content {
margin-top: 30px;
display: flex;
justify-content: center;
import { Component, ElementRef, Input, ViewChild } from '@angular/core'
import { VideoDetails } from '../../../shared/video/video-details.model'
import { buildVideoEmbed, buildVideoLink } from '../../../../assets/player/utils'
-import { NgbModal, NgbTabChangeEvent } from '@ng-bootstrap/ng-bootstrap'
+import { NgbModal, NgbNavChangeEvent, NgbTabChangeEvent } from '@ng-bootstrap/ng-bootstrap'
import { VideoCaption } from '@shared/models'
import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
@Input() videoCaptions: VideoCaption[] = []
@Input() playlist: VideoPlaylist = null
- activeId: 'url' | 'qrcode' | 'embed'
+ activeId: 'url' | 'qrcode' | 'embed' = 'url'
customizations: Customizations
isAdvancedCustomizationCollapsed = true
includeVideoInPlaylist = false
return window.location.protocol === 'http:'
}
- onTabChange (event: NgbTabChangeEvent) {
- this.activeId = event.nextId as any
- }
-
isInEmbedTab () {
return this.activeId === 'embed'
}
background-color: var(--mainHoverColor);
opacity: .9;
}
-
+
&::after {
display: none;
}
}
}
-ngb-tabset {
+.nav-tabs {
.nav-link {
- &, & a {
- @include disable-default-a-behaviour;
-
- color: var(--mainForegroundColor) !important;
- }
- }
+ @include disable-default-a-behaviour;
- .nav-pills .nav-link.active {
- color: #000 !important;
+ color: var(--mainForegroundColor) !important;
}
}