"@angular/service-worker": "~6.1.0",
"@angularclass/hmr": "^2.1.3",
"@neos21/bootstrap3-glyphicons": "^1.0.1",
+ "@ng-bootstrap/ng-bootstrap": "^2.2.2",
"@ngx-loading-bar/core": "^2.0.0",
"@ngx-loading-bar/http-client": "^2.0.0",
"@ngx-loading-bar/router": "^2.0.0",
"linkifyjs": "^2.1.5",
"lodash-es": "^4.17.4",
"markdown-it": "^8.4.0",
- "ngx-bootstrap": "3.0.1",
"ngx-chips": "1.9.3",
"ngx-clipboard": "11.1.1",
"ngx-pipes": "^2.1.7",
import { NgModule } from '@angular/core'
import { ConfigComponent, EditCustomConfigComponent } from '@app/+admin/config'
import { ConfigService } from '@app/+admin/config/shared/config.service'
-import { TabsModule } from 'ngx-bootstrap/tabs'
import { TableModule } from 'primeng/table'
import { SharedModule } from '../shared'
import { AdminRoutingModule } from './admin-routing.module'
@NgModule({
imports: [
AdminRoutingModule,
- TabsModule.forRoot(),
TableModule,
SharedModule
],
<form role="form" [formGroup]="form">
- <tabset class="root-tabset bootstrap">
+ <ngb-tabset class="root-tabset bootstrap">
- <tab i18n-heading heading="Basic configuration">
+ <ngb-tab i18n-title title="Basic configuration">
+ <ng-template ngbTabContent>
- <div i18n class="inner-form-title">Instance</div>
+ <div i18n class="inner-form-title">Instance</div>
- <div class="form-group">
- <label i18n for="instanceName">Name</label>
- <input
- type="text" id="instanceName"
- formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }"
- >
- <div *ngIf="formErrors.instanceName" class="form-error">
- {{ formErrors.instanceName }}
- </div>
- </div>
-
- <div class="form-group">
- <label i18n for="instanceShortDescription">Short description</label>
- <textarea
- id="instanceShortDescription" formControlName="instanceShortDescription"
- [ngClass]="{ 'input-error': formErrors['instanceShortDescription'] }"
- ></textarea>
- <div *ngIf="formErrors.instanceShortDescription" class="form-error">
- {{ formErrors.instanceShortDescription }}
- </div>
- </div>
-
- <div class="form-group">
- <label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
- <my-markdown-textarea
- id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true"
- [classes]="{ 'input-error': formErrors['instanceDescription'] }"
- ></my-markdown-textarea>
- <div *ngIf="formErrors.instanceDescription" class="form-error">
- {{ formErrors.instanceDescription }}
- </div>
- </div>
-
- <div class="form-group">
- <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
- <my-markdown-textarea
- id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true"
- [ngClass]="{ 'input-error': formErrors['instanceTerms'] }"
- ></my-markdown-textarea>
- <div *ngIf="formErrors.instanceTerms" class="form-error">
- {{ formErrors.instanceTerms }}
- </div>
- </div>
-
- <div class="form-group">
- <label i18n for="instanceDefaultClientRoute">Default client route</label>
- <div class="peertube-select-container">
- <select id="instanceDefaultClientRoute" formControlName="instanceDefaultClientRoute">
- <option i18n value="/videos/trending">Videos Trending</option>
- <option i18n value="/videos/recently-added">Videos Recently Added</option>
- <option i18n value="/videos/local">Local videos</option>
- </select>
- </div>
- <div *ngIf="formErrors.instanceDefaultClientRoute" class="form-error">
- {{ formErrors.instanceDefaultClientRoute }}
- </div>
- </div>
-
- <div class="form-group">
- <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
- <my-help
- helpType="custom" i18n-customHtml
- customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
- ></my-help>
-
- <div class="peertube-select-container">
- <select id="instanceDefaultNSFWPolicy" formControlName="instanceDefaultNSFWPolicy">
- <option i18n value="do_not_list">Do not list</option>
- <option i18n value="blur">Blur thumbnails</option>
- <option i18n value="display">Display</option>
- </select>
+ <div class="form-group">
+ <label i18n for="instanceName">Name</label>
+ <input
+ type="text" id="instanceName"
+ formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }"
+ >
+ <div *ngIf="formErrors.instanceName" class="form-error">
+ {{ formErrors.instanceName }}
+ </div>
</div>
- <div *ngIf="formErrors.instanceDefaultNSFWPolicy" class="form-error">
- {{ formErrors.instanceDefaultNSFWPolicy }}
+
+ <div class="form-group">
+ <label i18n for="instanceShortDescription">Short description</label>
+ <textarea
+ id="instanceShortDescription" formControlName="instanceShortDescription"
+ [ngClass]="{ 'input-error': formErrors['instanceShortDescription'] }"
+ ></textarea>
+ <div *ngIf="formErrors.instanceShortDescription" class="form-error">
+ {{ formErrors.instanceShortDescription }}
+ </div>
</div>
- </div>
-
- <div i18n class="inner-form-title">Signup</div>
-
- <my-peertube-checkbox
- inputName="signupEnabled" formControlName="signupEnabled"
- i18n-labelText labelText="Signup enabled"
- ></my-peertube-checkbox>
-
- <div *ngIf="isSignupEnabled()" class="form-group">
- <label i18n for="signupLimit">Signup limit</label>
- <input
- type="text" id="signupLimit"
- formControlName="signupLimit" [ngClass]="{ 'input-error': formErrors['signupLimit'] }"
- >
- <div *ngIf="formErrors.signupLimit" class="form-error">
- {{ formErrors.signupLimit }}
+
+ <div class="form-group">
+ <label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
+ <my-markdown-textarea
+ id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true"
+ [classes]="{ 'input-error': formErrors['instanceDescription'] }"
+ ></my-markdown-textarea>
+ <div *ngIf="formErrors.instanceDescription" class="form-error">
+ {{ formErrors.instanceDescription }}
+ </div>
</div>
- </div>
-
- <div i18n class="inner-form-title">Import</div>
-
- <my-peertube-checkbox
- inputName="importVideosHttpEnabled" formControlName="importVideosHttpEnabled"
- i18n-labelText labelText="Video import with HTTP enabled"
- ></my-peertube-checkbox>
-
- <my-peertube-checkbox
- inputName="importVideosTorrentEnabled" formControlName="importVideosTorrentEnabled"
- i18n-labelText labelText="Video import with a torrent file or a magnet URI enabled"
- ></my-peertube-checkbox>
-
- <div i18n class="inner-form-title">Administrator</div>
-
- <div class="form-group">
- <label i18n for="adminEmail">Admin email</label>
- <input
- type="text" id="adminEmail"
- formControlName="adminEmail" [ngClass]="{ 'input-error': formErrors['adminEmail'] }"
- >
- <div *ngIf="formErrors.adminEmail" class="form-error">
- {{ formErrors.adminEmail }}
+
+ <div class="form-group">
+ <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
+ <my-markdown-textarea
+ id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true"
+ [ngClass]="{ 'input-error': formErrors['instanceTerms'] }"
+ ></my-markdown-textarea>
+ <div *ngIf="formErrors.instanceTerms" class="form-error">
+ {{ formErrors.instanceTerms }}
+ </div>
</div>
- </div>
-
- <div i18n class="inner-form-title">Users</div>
-
- <div class="form-group">
- <label i18n for="userVideoQuota">User default video quota</label>
- <div class="peertube-select-container">
- <select id="userVideoQuota" formControlName="userVideoQuota">
- <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
- {{ videoQuotaOption.label }}
- </option>
- </select>
+
+ <div class="form-group">
+ <label i18n for="instanceDefaultClientRoute">Default client route</label>
+ <div class="peertube-select-container">
+ <select id="instanceDefaultClientRoute" formControlName="instanceDefaultClientRoute">
+ <option i18n value="/videos/trending">Videos Trending</option>
+ <option i18n value="/videos/recently-added">Videos Recently Added</option>
+ <option i18n value="/videos/local">Local videos</option>
+ </select>
+ </div>
+ <div *ngIf="formErrors.instanceDefaultClientRoute" class="form-error">
+ {{ formErrors.instanceDefaultClientRoute }}
+ </div>
</div>
- <div *ngIf="formErrors.userVideoQuota" class="form-error">
- {{ formErrors.userVideoQuota }}
+
+ <div class="form-group">
+ <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
+ <my-help
+ helpType="custom" i18n-customHtml
+ customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
+ ></my-help>
+
+ <div class="peertube-select-container">
+ <select id="instanceDefaultNSFWPolicy" formControlName="instanceDefaultNSFWPolicy">
+ <option i18n value="do_not_list">Do not list</option>
+ <option i18n value="blur">Blur thumbnails</option>
+ <option i18n value="display">Display</option>
+ </select>
+ </div>
+ <div *ngIf="formErrors.instanceDefaultNSFWPolicy" class="form-error">
+ {{ formErrors.instanceDefaultNSFWPolicy }}
+ </div>
</div>
- </div>
- </tab>
-
- <tab i18n-heading heading="Services">
-
- <div i18n class="inner-form-title">Twitter</div>
-
- <div class="form-group">
- <label i18n for="signupLimit">Your Twitter username</label>
- <my-help
- helpType="custom" i18n-customHtml
- customHtml="Indicates the Twitter account for the website or platform on which the content was published."
- ></my-help>
- <input
- type="text" id="servicesTwitterUsername"
- formControlName="servicesTwitterUsername" [ngClass]="{ 'input-error': formErrors['servicesTwitterUsername'] }"
- >
- <div *ngIf="formErrors.servicesTwitterUsername" class="form-error">
- {{ formErrors.servicesTwitterUsername }}
+
+ <div i18n class="inner-form-title">Signup</div>
+
+ <my-peertube-checkbox
+ inputName="signupEnabled" formControlName="signupEnabled"
+ i18n-labelText labelText="Signup enabled"
+ ></my-peertube-checkbox>
+
+ <div *ngIf="isSignupEnabled()" class="form-group">
+ <label i18n for="signupLimit">Signup limit</label>
+ <input
+ type="text" id="signupLimit"
+ formControlName="signupLimit" [ngClass]="{ 'input-error': formErrors['signupLimit'] }"
+ >
+ <div *ngIf="formErrors.signupLimit" class="form-error">
+ {{ formErrors.signupLimit }}
+ </div>
</div>
- </div>
- <my-peertube-checkbox
- inputName="servicesTwitterWhitelisted" formControlName="servicesTwitterWhitelisted"
- i18n-labelText labelText="Instance whitelisted by Twitter"
- i18n-helpHtml helpHtml="If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
-If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
-Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a> to see if you instance is whitelisted."
- ></my-peertube-checkbox>
- </tab>
+ <div i18n class="inner-form-title">Import</div>
+
+ <my-peertube-checkbox
+ inputName="importVideosHttpEnabled" formControlName="importVideosHttpEnabled"
+ i18n-labelText labelText="Video import with HTTP enabled"
+ ></my-peertube-checkbox>
- <tab i18n-heading heading="Advanced configuration">
+ <my-peertube-checkbox
+ inputName="importVideosTorrentEnabled" formControlName="importVideosTorrentEnabled"
+ i18n-labelText labelText="Video import with a torrent file or a magnet URI enabled"
+ ></my-peertube-checkbox>
- <div i18n class="inner-form-title">Transcoding</div>
+ <div i18n class="inner-form-title">Administrator</div>
- <my-peertube-checkbox
- inputName="transcodingEnabled" formControlName="transcodingEnabled"
- i18n-labelText labelText="Transcoding enabled"
- i18n-helpHtml helpHtml="If you disable transcoding, many videos from your users will not work!"
- ></my-peertube-checkbox>
+ <div class="form-group">
+ <label i18n for="adminEmail">Admin email</label>
+ <input
+ type="text" id="adminEmail"
+ formControlName="adminEmail" [ngClass]="{ 'input-error': formErrors['adminEmail'] }"
+ >
+ <div *ngIf="formErrors.adminEmail" class="form-error">
+ {{ formErrors.adminEmail }}
+ </div>
+ </div>
- <ng-template [ngIf]="isTranscodingEnabled()">
+ <div i18n class="inner-form-title">Users</div>
<div class="form-group">
- <label i18n for="transcodingThreads">Transcoding threads</label>
+ <label i18n for="userVideoQuota">User default video quota</label>
<div class="peertube-select-container">
- <select id="transcodingThreads" formControlName="transcodingThreads">
- <option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
- {{ transcodingThreadOption.label }}
+ <select id="userVideoQuota" formControlName="userVideoQuota">
+ <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
+ {{ videoQuotaOption.label }}
</option>
</select>
</div>
- <div *ngIf="formErrors.transcodingThreads" class="form-error">
- {{ formErrors.transcodingThreads }}
+ <div *ngIf="formErrors.userVideoQuota" class="form-error">
+ {{ formErrors.userVideoQuota }}
</div>
</div>
+ </ng-template>
+ </ngb-tab>
- <div class="form-group" *ngFor="let resolution of resolutions">
- <my-peertube-checkbox
- [inputName]="getResolutionKey(resolution)" [formControlName]="getResolutionKey(resolution)"
- i18n-labelText labelText="Resolution {{resolution}} enabled"
- ></my-peertube-checkbox>
+ <ngb-tab i18n-title title="Services">
+ <ng-template ngbTabContent>
+ <div i18n class="inner-form-title">Twitter</div>
+ <div class="form-group">
+ <label i18n for="signupLimit">Your Twitter username</label>
+ <my-help
+ helpType="custom" i18n-customHtml
+ customHtml="Indicates the Twitter account for the website or platform on which the content was published."
+ ></my-help>
+ <input
+ type="text" id="servicesTwitterUsername"
+ formControlName="servicesTwitterUsername" [ngClass]="{ 'input-error': formErrors['servicesTwitterUsername'] }"
+ >
+ <div *ngIf="formErrors.servicesTwitterUsername" class="form-error">
+ {{ formErrors.servicesTwitterUsername }}
+ </div>
</div>
- </ng-template>
- <div i18n class="inner-form-title">
- Cache
-
- <my-help
- helpType="custom" i18n-customHtml
- customHtml="Some files are not federated (previews, captions). We fetch them directly from the origin instance and cache them."
- ></my-help>
- </div>
-
- <div class="form-group">
- <label i18n for="cachePreviewsSize">Previews cache size</label>
- <input
- type="text" id="cachePreviewsSize"
- formControlName="cachePreviewsSize" [ngClass]="{ 'input-error': formErrors['cachePreviewsSize'] }"
- >
- <div *ngIf="formErrors.cachePreviewsSize" class="form-error">
- {{ formErrors.cachePreviewsSize }}
+ <my-peertube-checkbox
+ inputName="servicesTwitterWhitelisted" formControlName="servicesTwitterWhitelisted"
+ i18n-labelText labelText="Instance whitelisted by Twitter"
+ i18n-helpHtml helpHtml="If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
+ If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
+ Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a> to see if you instance is whitelisted."
+ ></my-peertube-checkbox>
+ </ng-template>
+ </ngb-tab>
+
+ <ngb-tab i18n-title title="Advanced configuration">
+ <ng-template ngbTabContent>
+
+ <div i18n class="inner-form-title">Transcoding</div>
+
+ <my-peertube-checkbox
+ inputName="transcodingEnabled" formControlName="transcodingEnabled"
+ i18n-labelText labelText="Transcoding enabled"
+ i18n-helpHtml helpHtml="If you disable transcoding, many videos from your users will not work!"
+ ></my-peertube-checkbox>
+
+ <ng-template [ngIf]="isTranscodingEnabled()">
+
+ <div class="form-group">
+ <label i18n for="transcodingThreads">Transcoding threads</label>
+ <div class="peertube-select-container">
+ <select id="transcodingThreads" formControlName="transcodingThreads">
+ <option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
+ {{ transcodingThreadOption.label }}
+ </option>
+ </select>
+ </div>
+ <div *ngIf="formErrors.transcodingThreads" class="form-error">
+ {{ formErrors.transcodingThreads }}
+ </div>
+ </div>
+
+ <div class="form-group" *ngFor="let resolution of resolutions">
+ <my-peertube-checkbox
+ [inputName]="getResolutionKey(resolution)" [formControlName]="getResolutionKey(resolution)"
+ i18n-labelText labelText="Resolution {{resolution}} enabled"
+ ></my-peertube-checkbox>
+
+ </div>
+ </ng-template>
+
+ <div i18n class="inner-form-title">
+ Cache
+
+ <my-help
+ helpType="custom" i18n-customHtml
+ customHtml="Some files are not federated (previews, captions). We fetch them directly from the origin instance and cache them."
+ ></my-help>
</div>
- </div>
-
- <div class="form-group">
- <label i18n for="cachePreviewsSize">Video captions cache size</label>
- <input
- type="text" id="cacheCaptionsSize"
- formControlName="cacheCaptionsSize" [ngClass]="{ 'input-error': formErrors['cacheCaptionsSize'] }"
- >
- <div *ngIf="formErrors.cacheCaptionsSize" class="form-error">
- {{ formErrors.cacheCaptionsSize }}
+
+ <div class="form-group">
+ <label i18n for="cachePreviewsSize">Previews cache size</label>
+ <input
+ type="text" id="cachePreviewsSize"
+ formControlName="cachePreviewsSize" [ngClass]="{ 'input-error': formErrors['cachePreviewsSize'] }"
+ >
+ <div *ngIf="formErrors.cachePreviewsSize" class="form-error">
+ {{ formErrors.cachePreviewsSize }}
+ </div>
</div>
- </div>
-
- <div i18n class="inner-form-title">Customizations</div>
-
- <div class="form-group">
- <label i18n for="customizationJavascript">JavaScript</label>
- <my-help
- helpType="custom" i18n-customHtml
- customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>"
- ></my-help>
- <textarea
- id="customizationJavascript" formControlName="customizationJavascript"
- [ngClass]="{ 'input-error': formErrors['customizationJavascript'] }"
- ></textarea>
- <div *ngIf="formErrors.customizationJavascript" class="form-error">
- {{ formErrors.customizationJavascript }}
+
+ <div class="form-group">
+ <label i18n for="cachePreviewsSize">Video captions cache size</label>
+ <input
+ type="text" id="cacheCaptionsSize"
+ formControlName="cacheCaptionsSize" [ngClass]="{ 'input-error': formErrors['cacheCaptionsSize'] }"
+ >
+ <div *ngIf="formErrors.cacheCaptionsSize" class="form-error">
+ {{ formErrors.cacheCaptionsSize }}
+ </div>
</div>
- </div>
-
- <div class="form-group">
- <label for="customizationCSS">CSS</label>
- <my-help
- helpType="custom"
- i18n-customHtml
- customHtml="
- Write directly CSS code. Example:<br />
- <pre>
- body {{ '{' }}
- background-color: red;
- {{ '}' }}
- </pre>
-
- Prepend with <em>#custom-css</em> to override styles. Example:
- <pre>
- #custom-css .logged-in-email {{ '{' }}
- color: red;
- {{ '}' }}
- </pre>
- "
- ></my-help>
- <textarea
- id="customizationCSS" formControlName="customizationCSS"
- [ngClass]="{ 'input-error': formErrors['customizationCSS'] }"
- ></textarea>
- <div *ngIf="formErrors.customizationCSS" class="form-error">
- {{ formErrors.customizationCSS }}
+
+ <div i18n class="inner-form-title">Customizations</div>
+
+ <div class="form-group">
+ <label i18n for="customizationJavascript">JavaScript</label>
+ <my-help
+ helpType="custom" i18n-customHtml
+ customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>"
+ ></my-help>
+ <textarea
+ id="customizationJavascript" formControlName="customizationJavascript"
+ [ngClass]="{ 'input-error': formErrors['customizationJavascript'] }"
+ ></textarea>
+ <div *ngIf="formErrors.customizationJavascript" class="form-error">
+ {{ formErrors.customizationJavascript }}
+ </div>
</div>
- </div>
- </tab>
- </tabset>
+
+ <div class="form-group">
+ <label for="customizationCSS">CSS</label>
+ <my-help
+ helpType="custom"
+ i18n-customHtml
+ customHtml="
+ Write directly CSS code. Example:<br />
+ <pre>
+ body {{ '{' }}
+ background-color: red;
+ {{ '}' }}
+ </pre>
+
+ Prepend with <em>#custom-css</em> to override styles. Example:
+ <pre>
+ #custom-css .logged-in-email {{ '{' }}
+ color: red;
+ {{ '}' }}
+ </pre>
+ "
+ ></my-help>
+ <textarea
+ id="customizationCSS" formControlName="customizationCSS"
+ [ngClass]="{ 'input-error': formErrors['customizationCSS'] }"
+ ></textarea>
+ <div *ngIf="formErrors.customizationCSS" class="form-error">
+ {{ formErrors.customizationCSS }}
+ </div>
+ </div>
+ </ng-template>
+ </ngb-tab>
+ </ngb-tabset>
<input (click)="formValidated()" type="submit" i18n-value value="Update configuration" [disabled]="!form.valid">
<span class="form-error" i18n *ngIf="!form.valid">It seems the configuration is invalid. Please search potential errors in the different tabs.</span>
It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
</div>
- <input type="submit" i18n-value value="Add following" [disabled]="hostsError || !hostsString" class="btn btn-default">
+ <input type="submit" i18n-value value="Add following" [disabled]="hostsError || !hostsString" class="btn btn-secondary">
</form>
<div class="admin-sub-header">
<div i18n class="form-sub-title">Manage follows</div>
- <tabset #followsMenuTabs>
- <tab *ngFor="let link of links">
- <ng-template tabHeading>
+ <ngb-tabset #followsMenuTabs type="pills">
+
+ <ngb-tab *ngFor="let link of links">
+ <ng-template ngbTabTitle>
<a class="tab-link" [routerLink]="link.path">{{ link.title }}</a>
</ng-template>
- </tab>
- </tabset>
+ </ngb-tab>
+
+ </ngb-tabset>
</div>
<router-outlet></router-outlet>
flex-grow: 0;
margin-right: 30px;
}
-
-/deep/ .tab-content {
- height: 0;
- min-height: 0;
- padding: 0;
-}
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'
import { NavigationEnd, Router } from '@angular/router'
-import { TabsetComponent } from 'ngx-bootstrap/tabs'
import { I18n } from '@ngx-translate/i18n-polyfill'
+import { NgbTabset } from '@ng-bootstrap/ng-bootstrap'
@Component({
templateUrl: './follows.component.html',
styleUrls: [ './follows.component.scss' ]
})
export class FollowsComponent implements OnInit, AfterViewInit {
- @ViewChild('followsMenuTabs') followsMenuTabs: TabsetComponent
+ @ViewChild('followsMenuTabs') followsMenuTabs: NgbTabset
links: { path: string, title: string }[] = []
for (let i = 0; i < this.links.length; i++) {
const path = this.links[i].path
- if (url.endsWith(path) === true && this.followsMenuTabs.tabs[i]) {
- this.followsMenuTabs.tabs[i].active = true
+ if (url.endsWith(path) === true) {
+ this.followsMenuTabs.select(path)
return
}
}
-<div bsModal #confirmModal="bs-modal" [config]="{ animated: false }" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog">
- <div class="modal-content">
+<ng-template #confirmModal let-close="close" let-dismiss="dismiss">
- <div class="modal-header">
- <span class="close" aria-hidden="true" (click)="cancel()"></span>
- <h4 class="modal-title">{{ title }}</h4>
- </div>
+ <div class="modal-header">
+ <h4 class="modal-title">{{ title }}</h4>
+ <span class="close" aria-label="Close" role="button" (click)="dismiss()"></span>
+ </div>
- <div class="modal-body" >
- <div [innerHtml]="message"></div>
+ <div class="modal-body" >
+ <div [innerHtml]="message"></div>
- <div *ngIf="inputLabel && expectedInputValue" class="form-group">
- <label for="confirmInput">{{ inputLabel }}</label>
- <input type="text" id="confirmInput" name="confirmInput" [(ngModel)]="inputValue" />
- </div>
+ <div *ngIf="inputLabel && expectedInputValue" class="form-group">
+ <label for="confirmInput">{{ inputLabel }}</label>
+ <input type="text" id="confirmInput" name="confirmInput" [(ngModel)]="inputValue" />
+ </div>
+ </div>
- <div class="form-group inputs">
- <span i18n class="action-button action-button-cancel" (click)="cancel()">
- Cancel
- </span>
+ <div class="modal-footer inputs">
+ <span i18n class="action-button action-button-cancel" (click)="dismiss()" role="button">Cancel</span>
- <input
- type="submit" [value]="confirmButtonText" class="action-button-submit" [disabled]="isConfirmationDisabled()"
- (click)="confirm()"
- >
- </div>
- </div>
- </div>
+ <input
+ type="submit" [value]="confirmButtonText" class="action-button-submit" [disabled]="isConfirmationDisabled()"
+ (click)="close()"
+ >
</div>
-</div>
+</ng-template>
-import { Component, HostListener, OnInit, ViewChild } from '@angular/core'
-
-import { ModalDirective } from 'ngx-bootstrap/modal'
-
+import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'
import { ConfirmService } from './confirm.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
+import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
@Component({
selector: 'my-confirm',
styleUrls: [ './confirm.component.scss' ]
})
export class ConfirmComponent implements OnInit {
- @ViewChild('confirmModal') confirmModal: ModalDirective
+ @ViewChild('confirmModal') confirmModal: ElementRef
title = ''
message = ''
inputValue = ''
confirmButtonText = ''
+ private openedModal: NgbModalRef
+
constructor (
+ private modalService: NgbModal,
private confirmService: ConfirmService,
private i18n: I18n
) {
}
ngOnInit () {
- this.confirmModal.config = {
- backdrop: 'static',
- keyboard: false
- }
-
this.confirmService.showConfirm.subscribe(
({ title, message, expectedInputValue, inputLabel, confirmButtonText }) => {
this.title = title
)
}
- @HostListener('keydown.enter')
+ @HostListener('document:keydown.enter')
confirm () {
- this.confirmService.confirmResponse.next(true)
- this.hideModal()
- }
-
- @HostListener('keydown.esc')
- cancel () {
- this.confirmService.confirmResponse.next(false)
- this.hideModal()
+ if (this.openedModal) this.openedModal.close()
}
isConfirmationDisabled () {
showModal () {
this.inputValue = ''
- this.confirmModal.show()
- }
- hideModal () {
- this.confirmModal.hide()
+ this.openedModal = this.modalService.open(this.confirmModal)
+
+ this.openedModal.result
+ .then(() => this.confirmService.confirmResponse.next(true))
+ .catch(() => this.confirmService.confirmResponse.next(false))
}
}
import { LoadingBarRouterModule } from '@ngx-loading-bar/router'
import { SimpleNotificationsModule } from 'angular2-notifications'
-import { ModalModule } from 'ngx-bootstrap/modal'
import { AuthService } from './auth'
import { ConfirmComponent, ConfirmService } from './confirm'
FormsModule,
BrowserAnimationsModule,
- ModalModule,
SimpleNotificationsModule.forRoot(),
LoadingBarHttpClientModule,
</form>
</div>
-<div bsModal #forgotPasswordModal="bs-modal" (onShown)="onForgotPasswordModalShown()" class="modal" tabindex="-1">
- <div class="modal-dialog">
- <div class="modal-content">
-
- <div class="modal-header">
- <span class="close" aria-hidden="true" (click)="hideForgotPasswordModal()"></span>
- <h4 i18n class="modal-title">Forgot your password</h4>
- </div>
+<!--<ng-template #forgotPasswordModal (onShown)="onForgotPasswordModalShown()">-->
+<ng-template #forgotPasswordModal>
+ <div class="modal-header">
+ <h4 i18n class="modal-title">Forgot your password</h4>
+ <span class="close" aria-hidden="true" (click)="hideForgotPasswordModal()"></span>
+ </div>
- <div class="modal-body">
- <div class="form-group">
- <label i18n for="forgot-password-email">Email</label>
- <input
- type="email" id="forgot-password-email" i18n-placeholder placeholder="Email address" required
- [(ngModel)]="forgotPasswordEmail" #forgotPasswordEmailInput
- >
- </div>
+ <div class="modal-body">
+ <div class="form-group">
+ <label i18n for="forgot-password-email">Email</label>
+ <input
+ type="email" id="forgot-password-email" i18n-placeholder placeholder="Email address" required
+ [(ngModel)]="forgotPasswordEmail" #forgotPasswordEmailInput
+ >
+ </div>
+ </div>
- <div class="form-group inputs">
- <span i18n class="action-button action-button-cancel" (click)="hideForgotPasswordModal()">
- Cancel
- </span>
+ <div class="modal-footer inputs">
+ <span i18n class="action-button action-button-cancel" (click)="hideForgotPasswordModal()">Cancel</span>
- <input
- type="submit" i18n-value value="Send me an email to reset my password" class="action-button-submit"
- (click)="askResetPassword()" [disabled]="!forgotPasswordEmailInput.validity.valid"
- >
- </div>
- </div>
- </div>
+ <input
+ type="submit" i18n-value value="Send me an email to reset my password" class="action-button-submit"
+ (click)="askResetPassword()" [disabled]="!forgotPasswordEmailInput.validity.valid"
+ >
</div>
-</div>
+</ng-template>
import { RedirectService, ServerService } from '@app/core'
import { UserService } from '@app/shared'
import { NotificationsService } from 'angular2-notifications'
-import { ModalDirective } from 'ngx-bootstrap/modal'
import { AuthService } from '../core'
import { FormReactive } from '../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
import { LoginValidatorsService } from '@app/shared/forms/form-validators/login-validators.service'
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
@Component({
selector: 'my-login',
})
export class LoginComponent extends FormReactive implements OnInit {
- @ViewChild('forgotPasswordModal') forgotPasswordModal: ModalDirective
+ @ViewChild('forgotPasswordModal') forgotPasswordModal: ElementRef
@ViewChild('forgotPasswordEmailInput') forgotPasswordEmailInput: ElementRef
error: string = null
forgotPasswordEmail = ''
+ private openedForgotPasswordModal: NgbModalRef
+
constructor (
protected formValidatorService: FormValidatorService,
+ private modalService: NgbModal,
private loginValidatorsService: LoginValidatorsService,
private authService: AuthService,
private userService: UserService,
}
openForgotPasswordModal () {
- this.forgotPasswordModal.show()
+ this.openedForgotPasswordModal = this.modalService.open(this.forgotPasswordModal)
}
hideForgotPasswordModal () {
- this.forgotPasswordModal.hide()
+ this.openedForgotPasswordModal.close()
}
}
-<div bsModal #modal="bs-modal" class="modal" tabindex="-1">
- <div class="modal-dialog">
- <div class="modal-content">
-
- <div class="modal-header">
- <span class="close" aria-hidden="true" (click)="hide()"></span>
- <h4 i18n class="modal-title">Change the language</h4>
- </div>
+<ng-template #modal let-hide="close">
+ <div class="modal-header">
+ <h4 i18n class="modal-title">Change the language</h4>
+ <span class="close" aria-label="Close" role="button" (click)="hide()"></span>
+ </div>
- <div class="modal-body" *ngFor="let lang of languages">
- <a [href]="buildLanguageLink(lang)">{{ lang.label }}</a>
- </div>
- </div>
+ <div class="modal-body">
+ <a *ngFor="let lang of languages" [href]="buildLanguageLink(lang)">{{ lang.label }}</a>
</div>
-</div>
+</ng-template>
@import '_variables';
@import '_mixins';
-.modal {
- z-index: 10005;
-}
-
-.modal-title {
- text-align: center;
-}
-
.modal-body {
text-align: center;
a {
+ display: block;
font-size: 16px;
- margin-top: 10px;
+ margin: 15px;
}
}
\ No newline at end of file
-import { Component, ViewChild } from '@angular/core'
-import { ModalDirective } from 'ngx-bootstrap/modal'
+import { Component, ElementRef, ViewChild } from '@angular/core'
import { I18N_LOCALES } from '../../../../shared'
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
@Component({
selector: 'my-language-chooser',
styleUrls: [ './language-chooser.component.scss' ]
})
export class LanguageChooserComponent {
- @ViewChild('modal') modal: ModalDirective
+ @ViewChild('modal') modal: ElementRef
- languages: { [ id: string ]: string }[] = []
+ languages: { id: string, label: string }[] = []
- constructor () {
+ constructor (private modalService: NgbModal) {
this.languages = Object.keys(I18N_LOCALES)
.map(k => ({ id: k, label: I18N_LOCALES[k] }))
}
show () {
- this.modal.show()
- }
-
- hide () {
- this.modal.hide()
+ this.modalService.open(this.modal)
}
buildLanguageLink (lang: { id: string }) {
<div class="logged-in-email">{{ user.username }}</div>
</div>
- <div class="logged-in-more" dropdown placement="right" container="body">
- <span class="glyphicon glyphicon-option-vertical" dropdownToggle></span>
+ <div class="logged-in-more" ngbDropdown placement="bottom-right">
+ <span class="glyphicon glyphicon-option-vertical" ngbDropdownToggle role="button"></span>
- <ul *dropdownMenu class="dropdown-menu">
- <li>
- <a i18n [routerLink]="[ '/accounts', user.account?.nameWithHost ]" class="dropdown-item" title="My public profile">
- My public profile
- </a>
+ <div ngbDropdownMenu>
+ <a *ngIf="user.account" i18n [routerLink]="[ '/accounts', user.account.nameWithHost ]" class="dropdown-item">
+ My public profile
+ </a>
- <a i18n routerLink="/my-account" class="dropdown-item" title="My account">
- My account
- </a>
+ <a i18n routerLink="/my-account" class="dropdown-item">
+ My account
+ </a>
- <a i18n (click)="logout($event)" class="dropdown-item" title="Log out" href="#">
- Log out
- </a>
- </li>
- </ul>
+ <a i18n (click)="logout($event)" class="dropdown-item" href="#">
+ Log out
+ </a>
+ </div>
</div>
</div>
.glyphicon {
cursor: pointer;
font-size: 18px;
+
+ &::after {
+ border: none;
+ }
}
}
}
</div>
</div>
- <div class="results-filter" [collapse]="isSearchFilterCollapsed">
+ <div class="results-filter" [ngbCollapse]="isSearchFilterCollapsed">
<my-search-filters [advancedSearch]="advancedSearch" (filtered)="onFiltered()"></my-search-filters>
</div>
</div>
import { SearchService } from '@app/search/search.service'
import { SearchRoutingModule } from '@app/search/search-routing.module'
import { SearchFiltersComponent } from '@app/search/search-filters.component'
-import { CollapseModule } from 'ngx-bootstrap/collapse'
+import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'
@NgModule({
imports: [
SearchRoutingModule,
SharedModule,
- CollapseModule.forRoot()
+ NgbCollapseModule.forRoot()
],
declarations: [
id="description" name="description">
</textarea>
- <tabset *ngIf="arePreviewsDisplayed()" class="previews">
- <tab *ngIf="truncate !== undefined" i18n-heading heading="Truncated preview" [innerHTML]="truncatedPreviewHTML"></tab>
- <tab i18n-heading heading="Complete preview" [innerHTML]="previewHTML"></tab>
- </tabset>
+ <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>
+
+ <ngb-tab i18n-title title="Complete preview">
+ <ng-template ngbTabContent><div [innerHTML]="previewHTML"></div></ng-template>
+ </ngb-tab>
+ </ngb-tabset>
</div>
title="Get help"
i18n-title
[attr.aria-pressed]="isPopoverOpened"
- [popover]="tooltipTemplate"
+ [ngbPopover]="tooltipTemplate"
[placement]="tooltipPlacement"
- [outsideClick]="true"
(onHidden)="onPopoverHidden()"
(onShown)="onPopoverShown()"
></span>
import { InfiniteScrollerDirective } from '@app/shared/video/infinite-scroller.directive'
import { MarkdownService } from '@app/videos/shared'
-import { BsDropdownModule } from 'ngx-bootstrap/dropdown'
-import { ModalModule } from 'ngx-bootstrap/modal'
-import { PopoverModule } from 'ngx-bootstrap/popover'
-import { TabsModule } from 'ngx-bootstrap/tabs'
-import { TooltipModule } from 'ngx-bootstrap/tooltip'
import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes'
import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared'
import { PeertubeCheckboxComponent } from '@app/shared/forms/peertube-checkbox.component'
import { VideoImportService } from '@app/shared/video-import/video-import.service'
import { ActionDropdownComponent } from '@app/shared/buttons/action-dropdown.component'
+import { NgbDropdownModule, NgbModalModule, NgbPopoverModule, NgbTabsetModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
@NgModule({
imports: [
RouterModule,
HttpClientModule,
- BsDropdownModule.forRoot(),
- ModalModule.forRoot(),
- PopoverModule.forRoot(),
- TabsModule.forRoot(),
- TooltipModule.forRoot(),
+ NgbDropdownModule.forRoot(),
+ NgbModalModule.forRoot(),
+ NgbPopoverModule.forRoot(),
+ NgbTabsetModule.forRoot(),
+ NgbTooltipModule.forRoot(),
PrimeSharedModule,
NgPipesModule
RouterModule,
HttpClientModule,
- BsDropdownModule,
- ModalModule,
- PopoverModule,
- TabsModule,
- TooltipModule,
+ NgbDropdownModule,
+ NgbModalModule,
+ NgbPopoverModule,
+ NgbTabsetModule,
+ NgbTooltipModule,
+
PrimeSharedModule,
BytesPipe,
KeysPipe,
<div class="video-feed">
- <span *ngIf="syndicationItems.length !== 0" class="icon icon-syndication"
- [popover]="feedsList"
- placement="bottom"
- [outsideClick]="true">
- </span>
+ <span
+ *ngIf="syndicationItems.length !== 0" [ngbPopover]="feedsList" placement="bottom"
+ class="icon icon-syndication" role="button"
+ ></span>
<ng-template #feedsList>
- <div *ngFor="let item of syndicationItems">
- <a [href]="item.url" target="_blank" rel="noopener noreferrer">{{ item.label }}</a>
- </div>
+ <a *ngFor="let item of syndicationItems" [href]="item.url" target="_blank" rel="noopener noreferrer">{{ item.label }}</a>
</ng-template>
</div>
\ No newline at end of file
@include disable-default-a-behaviour;
color: black;
+ display: block;
}
.icon {
-<div bsModal #modal="bs-modal" class="modal" tabindex="-1">
- <div class="modal-dialog">
- <div class="modal-content" [formGroup]="form">
+<ng-template #modal>
+ <ng-container [formGroup]="form">
- <div class="modal-header">
- <span class="close" aria-hidden="true" (click)="hide()"></span>
- <h4 i18n class="modal-title">Add caption</h4>
- </div>
+ <div class="modal-header">
+ <h4 i18n class="modal-title">Add caption</h4>
+ <span class="close" aria-label="Close" role="button" (click)="hide()"></span>
+ </div>
- <div class="modal-body">
- <label i18n for="language">Language</label>
- <div class="peertube-select-container">
- <select id="language" formControlName="language">
- <option></option>
- <option *ngFor="let language of videoCaptionLanguages" [value]="language.id">{{ language.label }}</option>
- </select>
- </div>
+ <div class="modal-body">
+ <label i18n for="language">Language</label>
+ <div class="peertube-select-container">
+ <select id="language" formControlName="language">
+ <option></option>
+ <option *ngFor="let language of videoCaptionLanguages" [value]="language.id">{{ language.label }}</option>
+ </select>
+ </div>
- <div *ngIf="formErrors.language" class="form-error">
- {{ formErrors.language }}
- </div>
+ <div *ngIf="formErrors.language" class="form-error">
+ {{ formErrors.language }}
+ </div>
- <div class="caption-file">
- <my-reactive-file
- formControlName="captionfile" inputName="captionfile" i18n-inputLabel inputLabel="Select the caption file"
- [extensions]="videoCaptionExtensions" [maxFileSize]="videoCaptionMaxSize" [displayFilename]="true"
- ></my-reactive-file>
- </div>
+ <div class="caption-file">
+ <my-reactive-file
+ formControlName="captionfile" inputName="captionfile" i18n-inputLabel inputLabel="Select the caption file"
+ [extensions]="videoCaptionExtensions" [maxFileSize]="videoCaptionMaxSize" [displayFilename]="true"
+ ></my-reactive-file>
+ </div>
- <div *ngIf="isReplacingExistingCaption()" class="warning-replace-caption" i18n>
- This will replace an existing caption!
- </div>
+ <div *ngIf="isReplacingExistingCaption()" class="warning-replace-caption" i18n>
+ This will replace an existing caption!
+ </div>
+ </div>
- <div class="form-group inputs">
- <span i18n class="action-button action-button-cancel" (click)="hide()">
- Cancel
- </span>
+ <div class="modal-footer inputs">
+ <span i18n class="action-button action-button-cancel" (click)="hide()">
+ Cancel
+ </span>
- <input
- type="submit" i18n-value value="Add this caption" class="action-button-submit"
- [disabled]="!form.valid" (click)="addCaption()"
- >
- </div>
- </div>
+ <input
+ type="submit" i18n-value value="Add this caption" class="action-button-submit"
+ [disabled]="!form.valid" (click)="addCaption()"
+ >
</div>
- </div>
-</div>
+ </ng-container>
+</ng-template>
-import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
-import { ModalDirective } from 'ngx-bootstrap/modal'
+import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
import { FormReactive } from '@app/shared'
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
import { VideoCaptionsValidatorsService } from '@app/shared/forms/form-validators/video-captions-validators.service'
import { ServerService } from '@app/core'
import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model'
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
@Component({
selector: 'my-video-caption-add-modal',
@Output() captionAdded = new EventEmitter<VideoCaptionEdit>()
- @ViewChild('modal') modal: ModalDirective
+ @ViewChild('modal') modal: ElementRef
videoCaptionLanguages = []
+ private openedModal: NgbModalRef
private closingModal = false
constructor (
protected formValidatorService: FormValidatorService,
+ private modalService: NgbModal,
private serverService: ServerService,
private videoCaptionsValidatorsService: VideoCaptionsValidatorsService
) {
show () {
this.closingModal = false
- this.modal.show()
+ this.openedModal = this.modalService.open(this.modal, { keyboard: false })
}
hide () {
this.closingModal = true
-
- this.modal.hide()
+ this.openedModal.close()
}
isReplacingExistingCaption () {
-<div class="video-edit row" [formGroup]="form">
- <tabset class="root-tabset bootstrap">
-
- <tab i18n-heading heading="Basic info">
- <div class="col-md-8">
- <div class="form-group">
- <label i18n for="name">Title</label>
- <input type="text" id="name" formControlName="name" />
- <div *ngIf="formErrors.name" class="form-error">
- {{ formErrors.name }}
+<div class="video-edit" [formGroup]="form">
+ <ngb-tabset class="root-tabset bootstrap">
+
+ <ngb-tab i18n-title title="Basic info">
+ <ng-template ngbTabContent>
+ <div class="row">
+ <div class="col-md-8">
+ <div class="form-group">
+ <label i18n for="name">Title</label>
+ <input type="text" id="name" formControlName="name" />
+ <div *ngIf="formErrors.name" class="form-error">
+ {{ formErrors.name }}
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label i18n class="label-tags">Tags</label> <span i18n>(press Enter to add)</span>
+ <tag-input
+ [validators]="tagValidators" [errorMessages]="tagValidatorsMessages"
+ formControlName="tags" maxItems="5" modelAsStrings="true"
+ ></tag-input>
+ </div>
+
+ <div class="form-group">
+ <label i18n for="description">Description</label>
+ <my-help helpType="markdownText" i18n-preHtml preHtml="Video descriptions are truncated by default and require manual action to expand them."></my-help>
+ <my-markdown-textarea truncate="250" formControlName="description"></my-markdown-textarea>
+
+ <div *ngIf="formErrors.description" class="form-error">
+ {{ formErrors.description }}
+ </div>
+ </div>
</div>
- </div>
-
- <div class="form-group">
- <label i18n class="label-tags">Tags</label> <span i18n>(press Enter to add)</span>
- <tag-input
- [validators]="tagValidators" [errorMessages]="tagValidatorsMessages"
- formControlName="tags" maxItems="5" modelAsStrings="true"
- ></tag-input>
- </div>
- <div class="form-group">
- <label i18n for="description">Description</label>
- <my-help helpType="markdownText" i18n-preHtml preHtml="Video descriptions are truncated by default and require manual action to expand them."></my-help>
- <my-markdown-textarea truncate="250" formControlName="description"></my-markdown-textarea>
+ <div class="col-md-4">
+ <div class="form-group">
+ <label i18n>Channel</label>
+ <div class="peertube-select-container">
+ <select formControlName="channelId">
+ <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
+ </select>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label i18n for="category">Category</label>
+ <div class="peertube-select-container">
+ <select id="category" formControlName="category">
+ <option></option>
+ <option *ngFor="let category of videoCategories" [value]="category.id">{{ category.label }}</option>
+ </select>
+ </div>
+
+ <div *ngIf="formErrors.category" class="form-error">
+ {{ formErrors.category }}
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label i18n for="licence">Licence</label>
+ <div class="peertube-select-container">
+ <select id="licence" formControlName="licence">
+ <option></option>
+ <option *ngFor="let licence of videoLicences" [value]="licence.id">{{ licence.label }}</option>
+ </select>
+ </div>
+
+ <div *ngIf="formErrors.licence" class="form-error">
+ {{ formErrors.licence }}
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label i18n for="language">Language</label>
+ <div class="peertube-select-container">
+ <select id="language" formControlName="language">
+ <option></option>
+ <option *ngFor="let language of videoLanguages" [value]="language.id">{{ language.label }}</option>
+ </select>
+ </div>
+
+ <div *ngIf="formErrors.language" class="form-error">
+ {{ formErrors.language }}
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label i18n for="privacy">Privacy</label>
+ <div class="peertube-select-container">
+ <select id="privacy" formControlName="privacy">
+ <option></option>
+ <option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option>
+ <option *ngIf="schedulePublicationPossible" [value]="SPECIAL_SCHEDULED_PRIVACY">Scheduled</option>
+ </select>
+ </div>
+
+ <div *ngIf="formErrors.privacy" class="form-error">
+ {{ formErrors.privacy }}
+ </div>
+ </div>
+
+ <div *ngIf="schedulePublicationEnabled" class="form-group">
+ <label i18n for="schedulePublicationAt">Schedule publication ({{ calendarTimezone }})</label>
+ <p-calendar
+ id="schedulePublicationAt" formControlName="schedulePublicationAt" [dateFormat]="calendarDateFormat"
+ [locale]="calendarLocale" [minDate]="minScheduledDate" [showTime]="true" [hideOnDateTimeSelect]="true"
+ >
+ </p-calendar>
+
+ <div *ngIf="formErrors.schedulePublicationAt" class="form-error">
+ {{ formErrors.schedulePublicationAt }}
+ </div>
+ </div>
+
+ <my-peertube-checkbox
+ inputName="nsfw" formControlName="nsfw"
+ i18n-labelText labelText="This video contains mature or explicit content"
+ i18n-helpHtml helpHtml="Some instances do not list videos containing mature or explicit content by default."
+ ></my-peertube-checkbox>
+
+ <my-peertube-checkbox
+ inputName="commentsEnabled" formControlName="commentsEnabled"
+ i18n-labelText labelText="Enable video comments"
+ ></my-peertube-checkbox>
+
+ <my-peertube-checkbox
+ inputName="waitTranscoding" formControlName="waitTranscoding"
+ i18n-labelText labelText="Wait transcoding before publishing the video"
+ i18n-helpHtml helpHtml="If you decide not to wait for transcoding before publishing the video, it could be unplayable until transcoding ends."
+ ></my-peertube-checkbox>
- <div *ngIf="formErrors.description" class="form-error">
- {{ formErrors.description }}
</div>
</div>
- </div>
-
- <div class="col-md-4">
- <div class="form-group">
- <label i18n>Channel</label>
- <div class="peertube-select-container">
- <select formControlName="channelId">
- <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
- </select>
+ </ng-template>
+ </ngb-tab>
+
+ <ngb-tab i18n-title title="Captions">
+ <ng-template ngbTabContent>
+ <div class="captions">
+
+ <div class="captions-header">
+ <a (click)="openAddCaptionModal()" class="create-caption">
+ <span class="icon icon-add"></span>
+ <ng-container i18n>Add another caption</ng-container>
+ </a>
</div>
- </div>
- <div class="form-group">
- <label i18n for="category">Category</label>
- <div class="peertube-select-container">
- <select id="category" formControlName="category">
- <option></option>
- <option *ngFor="let category of videoCategories" [value]="category.id">{{ category.label }}</option>
- </select>
- </div>
+ <div class="form-group" *ngFor="let videoCaption of videoCaptions">
- <div *ngIf="formErrors.category" class="form-error">
- {{ formErrors.category }}
- </div>
- </div>
+ <div class="caption-entry">
+ <ng-container *ngIf="!videoCaption.action">
+ <a
+ i18n-title title="See the subtitle file" class="caption-entry-label" target="_blank" rel="noopener noreferrer"
+ [href]="videoCaption.captionPath"
+ >{{ videoCaption.language.label }}</a>
- <div class="form-group">
- <label i18n for="licence">Licence</label>
- <div class="peertube-select-container">
- <select id="licence" formControlName="licence">
- <option></option>
- <option *ngFor="let licence of videoLicences" [value]="licence.id">{{ licence.label }}</option>
- </select>
- </div>
+ <div class="caption-entry-state">Already uploaded ✔</div>
- <div *ngIf="formErrors.licence" class="form-error">
- {{ formErrors.licence }}
- </div>
- </div>
+ <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</span>
+ </ng-container>
- <div class="form-group">
- <label i18n for="language">Language</label>
- <div class="peertube-select-container">
- <select id="language" formControlName="language">
- <option></option>
- <option *ngFor="let language of videoLanguages" [value]="language.id">{{ language.label }}</option>
- </select>
- </div>
+ <ng-container *ngIf="videoCaption.action === 'CREATE'">
+ <span class="caption-entry-label">{{ videoCaption.language.label }}</span>
- <div *ngIf="formErrors.language" class="form-error">
- {{ formErrors.language }}
- </div>
- </div>
+ <div class="caption-entry-state caption-entry-state-create">Will be created on update</div>
- <div class="form-group">
- <label i18n for="privacy">Privacy</label>
- <div class="peertube-select-container">
- <select id="privacy" formControlName="privacy">
- <option></option>
- <option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option>
- <option *ngIf="schedulePublicationPossible" [value]="SPECIAL_SCHEDULED_PRIVACY">Scheduled</option>
- </select>
- </div>
+ <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel create</span>
+ </ng-container>
- <div *ngIf="formErrors.privacy" class="form-error">
- {{ formErrors.privacy }}
- </div>
- </div>
+ <ng-container *ngIf="videoCaption.action === 'REMOVE'">
+ <span class="caption-entry-label">{{ videoCaption.language.label }}</span>
- <div *ngIf="schedulePublicationEnabled" class="form-group">
- <label i18n for="schedulePublicationAt">Schedule publication ({{ calendarTimezone }})</label>
- <p-calendar
- id="schedulePublicationAt" formControlName="schedulePublicationAt" [dateFormat]="calendarDateFormat"
- [locale]="calendarLocale" [minDate]="minScheduledDate" [showTime]="true" [hideOnDateTimeSelect]="true"
- >
- </p-calendar>
+ <div class="caption-entry-state caption-entry-state-delete">Will be deleted on update</div>
- <div *ngIf="formErrors.schedulePublicationAt" class="form-error">
- {{ formErrors.schedulePublicationAt }}
+ <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel deletion</span>
+ </ng-container>
+ </div>
</div>
- </div>
- <my-peertube-checkbox
- inputName="nsfw" formControlName="nsfw"
- i18n-labelText labelText="This video contains mature or explicit content"
- i18n-helpHtml helpHtml="Some instances do not list videos containing mature or explicit content by default."
- ></my-peertube-checkbox>
-
- <my-peertube-checkbox
- inputName="commentsEnabled" formControlName="commentsEnabled"
- i18n-labelText labelText="Enable video comments"
- ></my-peertube-checkbox>
-
- <my-peertube-checkbox
- inputName="waitTranscoding" formControlName="waitTranscoding"
- i18n-labelText labelText="Wait transcoding before publishing the video"
- i18n-helpHtml helpHtml="If you decide not to wait for transcoding before publishing the video, it could be unplayable until transcoding ends."
- ></my-peertube-checkbox>
-
- </div>
- </tab>
-
- <tab i18n-heading heading="Captions">
- <div class="col-md-12 captions">
-
- <div class="captions-header">
- <a (click)="openAddCaptionModal()" class="create-caption">
- <span class="icon icon-add"></span>
- <ng-container i18n>Add another caption</ng-container>
- </a>
- </div>
-
- <div class="form-group" *ngFor="let videoCaption of videoCaptions">
-
- <div class="caption-entry">
- <ng-container *ngIf="!videoCaption.action">
- <a
- i18n-title title="See the subtitle file" class="caption-entry-label" target="_blank" rel="noopener noreferrer"
- [href]="videoCaption.captionPath"
- >{{ videoCaption.language.label }}</a>
-
- <div class="caption-entry-state">Already uploaded ✔</div>
-
- <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</span>
- </ng-container>
-
- <ng-container *ngIf="videoCaption.action === 'CREATE'">
- <span class="caption-entry-label">{{ videoCaption.language.label }}</span>
-
- <div class="caption-entry-state caption-entry-state-create">Will be created on update</div>
-
- <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel create</span>
- </ng-container>
-
- <ng-container *ngIf="videoCaption.action === 'REMOVE'">
- <span class="caption-entry-label">{{ videoCaption.language.label }}</span>
-
- <div class="caption-entry-state caption-entry-state-delete">Will be deleted on update</div>
-
- <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel deletion</span>
- </ng-container>
+ <div class="no-caption" *ngIf="videoCaptions?.length === 0">
+ No captions for now.
</div>
- </div>
-
- <div class="no-caption" *ngIf="videoCaptions?.length === 0">
- No captions for now.
- </div>
- </div>
- </tab>
-
- <tab i18n-heading heading="Advanced settings">
- <div class="col-md-12 advanced-settings">
- <div class="form-group">
- <my-video-image
- i18n-inputLabel inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile"
- previewWidth="200px" previewHeight="110px"
- ></my-video-image>
</div>
+ </ng-template>
+ </ngb-tab>
+
+ <ngb-tab i18n-title title="Advanced settings">
+ <ng-template ngbTabContent>
+ <div class="advanced-settings">
+ <div class="form-group">
+ <my-video-image
+ i18n-inputLabel inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile"
+ previewWidth="200px" previewHeight="110px"
+ ></my-video-image>
+ </div>
- <div class="form-group">
- <my-video-image
- i18n-inputLabel inputLabel="Upload preview" inputName="previewfile" formControlName="previewfile"
- previewWidth="360px" previewHeight="200px"
- ></my-video-image>
- </div>
+ <div class="form-group">
+ <my-video-image
+ i18n-inputLabel inputLabel="Upload preview" inputName="previewfile" formControlName="previewfile"
+ previewWidth="360px" previewHeight="200px"
+ ></my-video-image>
+ </div>
- <div class="form-group">
- <label i18n for="support">Support</label>
- <my-help helpType="markdownEnhanced" i18n-preHtml preHtml="Short text to tell people how they can support you (membership platform...)."></my-help>
- <my-markdown-textarea
- id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced"
- [classes]="{ 'input-error': formErrors['support'] }"
- ></my-markdown-textarea>
- <div *ngIf="formErrors.support" class="form-error">
- {{ formErrors.support }}
+ <div class="form-group">
+ <label i18n for="support">Support</label>
+ <my-help helpType="markdownEnhanced" i18n-preHtml preHtml="Short text to tell people how they can support you (membership platform...)."></my-help>
+ <my-markdown-textarea
+ id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced"
+ [classes]="{ 'input-error': formErrors['support'] }"
+ ></my-markdown-textarea>
+ <div *ngIf="formErrors.support" class="form-error">
+ {{ formErrors.support }}
+ </div>
</div>
</div>
- </div>
- </tab>
+ </ng-template>
+ </ngb-tab>
- </tabset>
+ </ngb-tabset>
</div>
border: none;
padding: 0;
outline: 0;
+ color: inherit;
+ font-weight: $font-semibold;
}
.icon.icon-validate {
/deep/ {
.root-tabset > .nav {
- margin-left: 15px;
margin-bottom: 15px;
-
- .nav-link {
- display: flex !important;
- align-items: center;
- height: 30px !important;
- padding: 0 15px !important;
- }
}
.ng2-tag-input {
import { NgModule } from '@angular/core'
-import { TabsModule } from 'ngx-bootstrap/tabs'
import { TagInputModule } from 'ngx-chips'
import { SharedModule } from '../../../shared/'
import { VideoEditComponent } from './video-edit.component'
exports: [
TagInputModule,
- TabsModule,
CalendarModule,
VideoEditComponent
-<div bsModal #modal="bs-modal" class="modal" tabindex="-1">
- <div class="modal-dialog">
- <div class="modal-content">
-
- <div class="modal-header">
- <span class="close" aria-hidden="true" (click)="hide()"></span>
- <h4 i18n class="modal-title">Download video</h4>
- </div>
-
- <div class="modal-body">
- <div class="peertube-select-container">
- <select [(ngModel)]="resolutionId">
- <option *ngFor="let file of video.files" [value]="file.resolution.id">{{ file.resolution.label }}</option>
- </select>
- </div>
+<ng-template #modal let-hide="close">
+ <div class="modal-header">
+ <h4 i18n class="modal-title">Download video</h4>
+ <span class="close" aria-hidden="true" (click)="hide()"></span>
+ </div>
- <div class="download-type">
- <div class="peertube-radio-container">
- <input type="radio" name="download" id="download-direct" [(ngModel)]="downloadType" value="direct">
- <label i18n for="download-direct">Direct download</label>
- </div>
-
- <div class="peertube-radio-container">
- <input type="radio" name="download" id="download-torrent" [(ngModel)]="downloadType" value="torrent">
- <label i18n for="download-torrent">Torrent (.torrent file)</label>
- </div>
+ <div class="modal-body">
+ <div class="peertube-select-container">
+ <select [(ngModel)]="resolutionId">
+ <option *ngFor="let file of video.files" [value]="file.resolution.id">{{ file.resolution.label }}</option>
+ </select>
+ </div>
- <div class="peertube-radio-container">
- <input type="radio" name="download" id="download-magnet" [(ngModel)]="downloadType" value="magnet">
- <label i18n for="download-magnet">Torrent (magnet link)</label>
- </div>
- </div>
+ <div class="download-type">
+ <div class="peertube-radio-container">
+ <input type="radio" name="download" id="download-direct" [(ngModel)]="downloadType" value="direct">
+ <label i18n for="download-direct">Direct download</label>
+ </div>
- <div class="form-group inputs">
- <span i18n class="action-button action-button-cancel" (click)="hide()">
- Cancel
- </span>
+ <div class="peertube-radio-container">
+ <input type="radio" name="download" id="download-torrent" [(ngModel)]="downloadType" value="torrent">
+ <label i18n for="download-torrent">Torrent (.torrent file)</label>
+ </div>
- <input
- type="submit" i18n-value value="Download" class="action-button-submit"
- (click)="download()"
- >
- </div>
+ <div class="peertube-radio-container">
+ <input type="radio" name="download" id="download-magnet" [(ngModel)]="downloadType" value="magnet">
+ <label i18n for="download-magnet">Torrent (magnet link)</label>
</div>
</div>
</div>
-</div>
+
+ <div class="modal-footer inputs">
+ <span i18n class="action-button action-button-cancel" (click)="hide()">
+ Cancel
+ </span>
+
+ <input
+ type="submit" i18n-value value="Download" class="action-button-submit"
+ (click)="download()"
+ >
+ </div>
+</ng-template>
-import { Component, Input, OnInit, ViewChild } from '@angular/core'
-import { ModalDirective } from 'ngx-bootstrap/modal'
+import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'
import { VideoDetails } from '../../../shared/video/video-details.model'
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
@Component({
selector: 'my-video-download',
export class VideoDownloadComponent implements OnInit {
@Input() video: VideoDetails = null
- @ViewChild('modal') modal: ModalDirective
+ @ViewChild('modal') modal: ElementRef
downloadType: 'direct' | 'torrent' | 'magnet' = 'torrent'
resolutionId: number | string = -1
- constructor () {
+ constructor (private modalService: NgbModal) {
// empty
}
}
show () {
- this.modal.show()
- }
-
- hide () {
- this.modal.hide()
+ this.modalService.open(this.modal)
}
download () {
-<div bsModal #modal="bs-modal" class="modal" tabindex="-1">
- <div class="modal-dialog">
- <div class="modal-content">
-
- <div class="modal-header">
- <span class="close" aria-hidden="true" (click)="hide()"></span>
- <h4 i18n class="modal-title">Report video</h4>
- </div>
-
- <div class="modal-body">
+<ng-template #modal>
+ <div class="modal-header">
+ <h4 i18n class="modal-title">Report video</h4>
+ <span class="close" aria-label="Close" role="button" (click)="hide()"></span>
+ </div>
- <form novalidate [formGroup]="form" (ngSubmit)="report()">
- <div class="form-group">
- <textarea i18n-placeholder placeholder="Reason..." formControlName="reason" [ngClass]="{ 'input-error': formErrors['reason'] }">
- </textarea>
- <div *ngIf="formErrors.reason" class="form-error">
- {{ formErrors.reason }}
- </div>
- </div>
+ <div class="modal-body">
- <div class="form-group inputs">
- <span i18n class="action-button action-button-cancel" (click)="hide()">
- Cancel
- </span>
+ <form novalidate [formGroup]="form" (ngSubmit)="report()">
+ <div class="form-group">
+ <textarea i18n-placeholder placeholder="Reason..." formControlName="reason" [ngClass]="{ 'input-error': formErrors['reason'] }">
+ </textarea>
+ <div *ngIf="formErrors.reason" class="form-error">
+ {{ formErrors.reason }}
+ </div>
+ </div>
- <input
- type="submit" i18n-value value="Submit" class="action-button-submit"
- [disabled]="!form.valid"
- >
- </div>
- </form>
+ <div class="form-group inputs">
+ <span i18n class="action-button action-button-cancel" (click)="hide()">
+ Cancel
+ </span>
+ <input
+ type="submit" i18n-value value="Submit" class="action-button-submit"
+ [disabled]="!form.valid"
+ >
</div>
- </div>
+ </form>
+
</div>
-</div>
+</ng-template>
import { Component, Input, OnInit, ViewChild } from '@angular/core'
import { NotificationsService } from 'angular2-notifications'
-import { ModalDirective } from 'ngx-bootstrap/modal'
import { FormReactive, VideoAbuseService } from '../../../shared/index'
import { VideoDetails } from '../../../shared/video/video-details.model'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
import { VideoAbuseValidatorsService } from '@app/shared/forms/form-validators/video-abuse-validators.service'
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
+import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
@Component({
selector: 'my-video-report',
export class VideoReportComponent extends FormReactive implements OnInit {
@Input() video: VideoDetails = null
- @ViewChild('modal') modal: ModalDirective
+ @ViewChild('modal') modal: NgbModal
error: string = null
+ private openedModal: NgbModalRef
+
constructor (
protected formValidatorService: FormValidatorService,
+ private modalService: NgbModal,
private videoAbuseValidatorsService: VideoAbuseValidatorsService,
private videoAbuseService: VideoAbuseService,
private notificationsService: NotificationsService,
}
show () {
- this.modal.show()
+ this.openedModal = this.modalService.open(this.modal, { keyboard: false })
}
hide () {
- this.modal.hide()
+ this.openedModal.close()
+ this.openedModal = null
}
report () {
-<div bsModal #modal="bs-modal" class="modal" tabindex="-1">
- <div class="modal-dialog">
- <div class="modal-content">
-
- <div class="modal-header">
- <span class="close" aria-hidden="true" (click)="hide()"></span>
- <h4 i18n class="modal-title">Share</h4>
- </div>
-
- <div class="modal-body">
- <div class="form-group">
- <label i18n>URL</label>
- <div class="input-group input-group-sm">
- <input #urlInput (click)="urlInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getVideoUrl()" />
- <div class="input-group-btn" placement="bottom right">
- <button [ngxClipboard]="urlInput" (click)="activateCopiedMessage()" type="button" class="btn btn-default btn-search">
- <span class="glyphicon glyphicon-copy"></span>
- </button>
- </div>
- </div>
- </div>
+<ng-template #modal let-hide="close">
+ <div class="modal-header">
+ <h4 i18n class="modal-title">Share</h4>
+ <span class="close" aria-hidden="true" (click)="hide()"></span>
+ </div>
- <div class="form-group">
- <label i18n>Embed</label>
- <div class="input-group input-group-sm">
- <input #shareInput (click)="shareInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getVideoIframeCode()" />
- <div class="input-group-btn" placement="bottom right">
- <button [ngxClipboard]="shareInput" (click)="activateCopiedMessage()" type="button" class="btn btn-default btn-search">
- <span class="glyphicon glyphicon-copy"></span>
- </button>
- </div>
- </div>
+ <div class="modal-body">
+ <div class="form-group">
+ <label i18n>URL</label>
+ <div class="input-group input-group-sm">
+ <input #urlInput (click)="urlInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getVideoUrl()" />
+ <div class="input-group-append">
+ <button [ngxClipboard]="urlInput" (click)="activateCopiedMessage()" type="button" class="btn btn-outline-secondary">
+ <span class="glyphicon glyphicon-copy"></span>
+ </button>
</div>
+ </div>
+ </div>
- <div i18n *ngIf="notSecure()" class="alert alert-warning">
- The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites).
+ <div class="form-group">
+ <label i18n>Embed</label>
+ <div class="input-group input-group-sm">
+ <input #shareInput (click)="shareInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getVideoIframeCode()" />
+ <div class="input-group-append">
+ <button [ngxClipboard]="shareInput" (click)="activateCopiedMessage()" type="button" class="btn btn-outline-secondary">
+ <span class="glyphicon glyphicon-copy"></span>
+ </button>
</div>
+ </div>
+ </div>
- <div class="form-group qr-code-group">
- <label i18n>QR-Code</label>
- <ngx-qrcode qrc-element-type="url" [qrc-value]="getVideoUrl()" qrc-errorCorrectionLevel="Q"></ngx-qrcode>
- </div>
+ <div i18n *ngIf="notSecure()" class="alert alert-warning">
+ The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites).
+ </div>
- <div class="form-group inputs">
- <span i18n class="action-button action-button-cancel" (click)="hide()">
- Cancel
- </span>
- </div>
- </div>
+ <div class="form-group qr-code-group">
+ <label i18n>QR-Code</label>
+ <ngx-qrcode qrc-element-type="url" [qrc-value]="getVideoUrl()" qrc-errorCorrectionLevel="Q"></ngx-qrcode>
</div>
</div>
-</div>
+
+ <div class="modal-footer inputs">
+ <span i18n class="action-button action-button-cancel" (click)="hide()">Cancel</span>
+ </div>
+
+</ng-template>
+@import '~bootstrap/scss/functions';
+@import '~bootstrap/scss/variables';
+
.action-button-cancel {
margin-right: 0 !important;
}
+.btn-outline-secondary {
+ border-color: $input-border-color;
+}
+
.qr-code-group {
text-align: center;
}
-import { Component, Input, ViewChild } from '@angular/core'
-
+import { Component, ElementRef, Input, ViewChild } from '@angular/core'
import { NotificationsService } from 'angular2-notifications'
-
-import { ModalDirective } from 'ngx-bootstrap/modal'
import { VideoDetails } from '../../../shared/video/video-details.model'
import { buildVideoEmbed } from '../../../../assets/player/utils'
import { I18n } from '@ngx-translate/i18n-polyfill'
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
@Component({
selector: 'my-video-share',
export class VideoShareComponent {
@Input() video: VideoDetails = null
- @ViewChild('modal') modal: ModalDirective
+ @ViewChild('modal') modal: ElementRef
constructor (
+ private modalService: NgbModal,
private notificationsService: NotificationsService,
private i18n: I18n
) {
}
show () {
- this.modal.show()
- }
-
- hide () {
- this.modal.hide()
+ this.modalService.open(this.modal)
}
getVideoIframeCode () {
-<div bsModal #modal="bs-modal" class="modal" tabindex="-1">
- <div class="modal-dialog">
- <div class="modal-content">
-
- <div class="modal-header">
- <span class="close" aria-hidden="true" (click)="hide()"></span>
- <h4 i18n class="modal-title">Support</h4>
- </div>
-
- <div class="modal-body">
+<ng-template #modal let-hide="close">
+ <div class="modal-header">
+ <h4 i18n class="modal-title">Support</h4>
+ <span class="close" aria-label="Close" role="button" (click)="hide()"></span>
+ </div>
- <div [innerHTML]="videoHTMLSupport"></div>
+ <div class="modal-body" [innerHTML]="videoHTMLSupport"></div>
- <div class="form-group inputs">
- <span i18n class="action-button action-button-cancel" (click)="hide()">
- Cancel
- </span>
- </div>
- </div>
- </div>
+ <div class="modal-footer inputs">
+ <span i18n class="action-button action-button-cancel" (click)="hide()">Cancel</span>
</div>
-</div>
+</ng-template>
import { Component, Input, ViewChild } from '@angular/core'
import { MarkdownService } from '@app/videos/shared'
-import { ModalDirective } from 'ngx-bootstrap/modal'
import { VideoDetails } from '../../../shared/video/video-details.model'
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
@Component({
selector: 'my-video-support',
export class VideoSupportComponent {
@Input() video: VideoDetails = null
- @ViewChild('modal') modal: ModalDirective
+ @ViewChild('modal') modal: NgbModal
videoHTMLSupport = ''
- constructor (private markdownService: MarkdownService) {
+ constructor (
+ private markdownService: MarkdownService,
+ private modalService: NgbModal
+ ) {
// empty
}
show () {
- this.modal.show()
-
this.videoHTMLSupport = this.markdownService.enhancedMarkdownToHTML(this.video.support)
- }
-
- hide () {
- this.modal.hide()
+ this.modalService.open(this.modal)
}
}
<span class="icon-text" i18n>Share</span>
</div>
- <div class="action-more" dropdown dropup="true" placement="right" role="button">
- <div class="action-button" dropdownToggle>
+ <div class="action-more" ngbDropdown placement="top" role="button">
+ <div class="action-button" ngbDropdownToggle role="button">
<span class="icon icon-more"></span>
</div>
- <ul *dropdownMenu class="dropdown-menu" id="more-menu" role="menu" aria-labelledby="single-button">
- <li role="menuitem">
- <a class="dropdown-item" i18n-title title="Download the video" href="#" (click)="showDownloadModal($event)">
- <span class="icon icon-download"></span> <ng-container i18n>Download</ng-container>
- </a>
- </li>
-
- <li *ngIf="isUserLoggedIn()" role="menuitem">
- <a class="dropdown-item" i18n-title title="Report this video" href="#" (click)="showReportModal($event)">
- <span class="icon icon-alert"></span> <ng-container i18n>Report</ng-container>
- </a>
- </li>
-
- <li *ngIf="isVideoBlacklistable()" role="menuitem">
- <a class="dropdown-item" i18n-title title="Blacklist this video" href="#" (click)="blacklistVideo($event)">
- <span class="icon icon-blacklist"></span> <ng-container i18n>Blacklist</ng-container>
- </a>
- </li>
-
- <li *ngIf="isVideoUpdatable()" role="menuitem">
- <a class="dropdown-item" i18n-title title="Update this video" href="#" [routerLink]="[ '/videos/update', video.uuid ]">
- <span class="icon icon-edit"></span> <ng-container i18n>Update</ng-container>
- </a>
- </li>
-
- <li *ngIf="isVideoRemovable()" role="menuitem">
- <a class="dropdown-item" i18n-title title="Delete this video" href="#" (click)="removeVideo($event)">
- <span class="icon icon-blacklist"></span> <ng-container i18n>Delete</ng-container>
- </a>
- </li>
- </ul>
+ <div ngbDropdownMenu>
+ <a class="dropdown-item" i18n-title title="Download the video" href="#" (click)="showDownloadModal($event)">
+ <span class="icon icon-download"></span> <ng-container i18n>Download</ng-container>
+ </a>
+
+ <a *ngIf="isUserLoggedIn()" class="dropdown-item" i18n-title title="Report this video" href="#" (click)="showReportModal($event)">
+ <span class="icon icon-alert"></span> <ng-container i18n>Report</ng-container>
+ </a>
+
+ <a *ngIf="isVideoBlacklistable()" class="dropdown-item" i18n-title title="Blacklist this video" href="#" (click)="blacklistVideo($event)">
+ <span class="icon icon-blacklist"></span> <ng-container i18n>Blacklist</ng-container>
+ </a>
+
+ <a *ngIf="isVideoUpdatable()" class="dropdown-item" i18n-title title="Update this video" href="#" [routerLink]="[ '/videos/update', video.uuid ]">
+ <span class="icon icon-edit"></span> <ng-container i18n>Update</ng-container>
+ </a>
+
+ <a *ngIf="isVideoRemovable()" class="dropdown-item" i18n-title title="Delete this video" href="#" (click)="removeVideo($event)">
+ <span class="icon icon-blacklist"></span> <ng-container i18n>Delete</ng-container>
+ </a>
+ </div>
</div>
</div>
<div
class="video-info-likes-dislikes-bar"
- *ngIf="video.likes !== 0 || video.dislikes !== 0" [tooltip]="likesBarTooltipText">
+ *ngIf="video.likes !== 0 || video.dislikes !== 0" [ngbTooltip]="likesBarTooltipText">
<div class="likes-bar" [ngStyle]="{ 'width.%': video.likesPercent }"></div>
</div>
</div>
padding: 0 10px 0 10px;
white-space: nowrap;
+ &::after {
+ display: none;
+ }
+
.icon {
@include icon(21px);
import { NgModule } from '@angular/core'
import { LinkifierService } from '@app/videos/+video-watch/comment/linkifier.service'
import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-support.component'
-import { TooltipModule } from 'ngx-bootstrap/tooltip'
import { ClipboardModule } from 'ngx-clipboard'
import { SharedModule } from '../../shared'
import { MarkdownService } from '../shared'
import { VideoDownloadComponent } from './modal/video-download.component'
import { VideoReportComponent } from './modal/video-report.component'
import { VideoShareComponent } from './modal/video-share.component'
-
import { VideoWatchRoutingModule } from './video-watch-routing.module'
-
import { VideoWatchComponent } from './video-watch.component'
import { NgxQRCodeModule } from 'ngx-qrcode2'
+import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
@NgModule({
imports: [
VideoWatchRoutingModule,
SharedModule,
ClipboardModule,
- TooltipModule.forRoot(),
+ NgbTooltipModule.forRoot(),
NgxQRCodeModule
],
background-color: #fff !important;
}
+input, textarea {
+ outline: none;
+}
+
label {
font-weight: $font-bold;
font-size: 15px;
@include icon(24px);
position: relative;
- right: -1px;
+ top: 3px;
float: right;
background-image: url('../assets/images/global/cross.svg');
+
+ margin: 0;
+ padding: 0;
+ opacity: 1;
}
}
}
}
-tabset:not(.bootstrap) {
- .nav {
- font-size: 16px !important;
- border: none !important;
+// Nav customizations
+.nav .nav-link {
+ display: flex !important;
+ align-items: center;
+ height: 30px !important;
+ padding: 10px 15px !important;
+}
- .nav-item .nav-link {
- margin-right: 30px;
- padding: 0;
- border-radius: 3px;
- border: none !important;
-
- .tab-link {
- display: flex !important;
- align-items: center;
- min-height: 30px !important;
- padding: 0 15px;
- }
+.nav.nav-pills {
+ font-size: 16px !important;
- &, & a {
- color: #000 !important;
- @include disable-default-a-behaviour;
- }
+ .nav-link.active {
+ font-weight: $font-semibold !important;
+ }
- &.active, &:hover {
- background-color: #F0F0F0;
- }
+ a {
+ @include disable-default-a-behaviour;
- &.active {
- font-weight: $font-semibold !important;
- }
- }
+ color: #000;
}
}
-tabset.bootstrap {
- margin-left: 0;
+ngb-tabset.bootstrap {
- .nav-item .nav-link {
+ .nav-link {
&, & a {
- color: #000;
@include disable-default-a-behaviour;
+
+ color: #000 !important;
}
}
}
-@import "~bootstrap/scss/functions";
-@import "~bootstrap/scss/variables";
-@import "~bootstrap/scss/mixins";
-@import "~bootstrap/scss/root";
-@import "~bootstrap/scss/reboot";
-@import "~bootstrap/scss/type";
-//@import "~bootstrap/scss/images";
-//@import "~bootstrap/scss/code";
-@import "~bootstrap/scss/grid";
-//@import "~bootstrap/scss/tables";
-@import "~bootstrap/scss/forms";
-@import "~bootstrap/scss/buttons";
-//@import "~bootstrap/scss/transitions";
-@import "~bootstrap/scss/dropdown";
-//@import "~bootstrap/scss/button-group";
-@import "~bootstrap/scss/input-group";
-//@import "~bootstrap/scss/custom-forms";
-@import "~bootstrap/scss/nav";
-//@import "~bootstrap/scss/navbar";
-//@import "~bootstrap/scss/card";
-//@import "~bootstrap/scss/breadcrumb";
-//@import "~bootstrap/scss/pagination";
-//@import "~bootstrap/scss/badge";
-//@import "~bootstrap/scss/jumbotron";
-@import "~bootstrap/scss/alert";
-//@import "~bootstrap/scss/progress";
-//@import "~bootstrap/scss/media";
-//@import "~bootstrap/scss/list-group";
-@import "~bootstrap/scss/close";
-@import "~bootstrap/scss/modal";
-@import "~bootstrap/scss/tooltip";
-@import "~bootstrap/scss/popover";
-//@import "~bootstrap/scss/carousel";
-//@import "~bootstrap/scss/utilities";
-//@import "~bootstrap/scss/print";
+$dropdown-link-active-bg: inherit;
-@import "~@neos21/bootstrap3-glyphicons/assets/stylesheets/bootstrap3-glyphicons";
\ No newline at end of file
+$zindex-modal: 10005;
+$modal-footer-border-width: 0;
+$modal-md: 600px;
+
+$input-btn-focus-width: 0;
+$input-btn-focus-color: inherit;
+$input-focus-border-color: #ced4da;
+
+$nav-pills-link-active-bg: #F0F0F0;
+$nav-pills-link-active-color: #000;
+
+@import '~bootstrap/scss/functions';
+@import '~bootstrap/scss/variables';
+
+@import '~bootstrap/scss/mixins';
+@import '~bootstrap/scss/root';
+@import '~bootstrap/scss/reboot';
+@import '~bootstrap/scss/type';
+//@import '~bootstrap/scss/images';
+//@import '~bootstrap/scss/code';
+@import '~bootstrap/scss/grid';
+//@import '~bootstrap/scss/tables';
+@import '~bootstrap/scss/forms';
+@import '~bootstrap/scss/buttons';
+//@import '~bootstrap/scss/transitions';
+@import '~bootstrap/scss/dropdown';
+//@import '~bootstrap/scss/button-group';
+@import '~bootstrap/scss/input-group';
+//@import '~bootstrap/scss/custom-forms';
+@import '~bootstrap/scss/nav';
+//@import '~bootstrap/scss/navbar';
+//@import '~bootstrap/scss/card';
+//@import '~bootstrap/scss/breadcrumb';
+//@import '~bootstrap/scss/pagination';
+//@import '~bootstrap/scss/badge';
+//@import '~bootstrap/scss/jumbotron';
+@import '~bootstrap/scss/alert';
+//@import '~bootstrap/scss/progress';
+//@import '~bootstrap/scss/media';
+//@import '~bootstrap/scss/list-group';
+@import '~bootstrap/scss/close';
+@import '~bootstrap/scss/modal';
+@import '~bootstrap/scss/tooltip';
+@import '~bootstrap/scss/popover';
+//@import '~bootstrap/scss/carousel';
+@import '~bootstrap/scss/utilities';
+//@import '~bootstrap/scss/print';
+
+@import '~@neos21/bootstrap3-glyphicons/assets/stylesheets/bootstrap3-glyphicons';
version "1.0.1"
resolved "https://registry.yarnpkg.com/@neos21/bootstrap3-glyphicons/-/bootstrap3-glyphicons-1.0.1.tgz#e5eeec43e0153d4b51effd9ecb58cdf7029924d7"
+"@ng-bootstrap/ng-bootstrap@^2.2.2":
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-2.2.2.tgz#07c64badd48b563140eb5a6327b5516bf2226834"
+
"@ngtools/webpack@6.1.1":
version "6.1.1"
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-6.1.1.tgz#dcfea287c0c1358f3e123621c65b0e3ccaab5b70"
dependencies:
tslib "^1.9.0"
-ngx-bootstrap@3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/ngx-bootstrap/-/ngx-bootstrap-3.0.1.tgz#e98d2fc6340f32a9d358cd08e8fda7dcb23bdab3"
-
ngx-chips@1.9.3:
version "1.9.3"
resolved "https://registry.yarnpkg.com/ngx-chips/-/ngx-chips-1.9.3.tgz#0ebc13b4868d9cd480478ed93fd56bcc3b68ea66"