add ng-select for templatable select options
- create select-tags component to replace ngx-chips - create select-options to factorize option selection in forms - create select-channel to simplify channel selection - refactor tags validation
This commit is contained in:
parent
766d13b447
commit
02c01341f4
|
@ -44,6 +44,7 @@
|
||||||
"@angularclass/hmr": "^2.1.3",
|
"@angularclass/hmr": "^2.1.3",
|
||||||
"@neos21/bootstrap3-glyphicons": "^1.0.1",
|
"@neos21/bootstrap3-glyphicons": "^1.0.1",
|
||||||
"@ng-bootstrap/ng-bootstrap": "^7.0.0",
|
"@ng-bootstrap/ng-bootstrap": "^7.0.0",
|
||||||
|
"@ng-select/ng-select": "^5.0.0",
|
||||||
"@ngx-i18nsupport/ngx-i18nsupport": "^1.1.6",
|
"@ngx-i18nsupport/ngx-i18nsupport": "^1.1.6",
|
||||||
"@ngx-i18nsupport/tooling": "^8.0.3",
|
"@ngx-i18nsupport/tooling": "^8.0.3",
|
||||||
"@ngx-loading-bar/core": "^5.0.0",
|
"@ngx-loading-bar/core": "^5.0.0",
|
||||||
|
@ -95,7 +96,6 @@
|
||||||
"linkifyjs": "^2.1.5",
|
"linkifyjs": "^2.1.5",
|
||||||
"lodash-es": "^4.17.4",
|
"lodash-es": "^4.17.4",
|
||||||
"markdown-it": "^11.0.0",
|
"markdown-it": "^11.0.0",
|
||||||
"ngx-chips": "2.1.0",
|
|
||||||
"ngx-pipes": "^2.6.0",
|
"ngx-pipes": "^2.6.0",
|
||||||
"node-sass": "^4.9.3",
|
"node-sass": "^4.9.3",
|
||||||
"npm-font-source-sans-pro": "^1.0.2",
|
"npm-font-source-sans-pro": "^1.0.2",
|
||||||
|
@ -133,6 +133,5 @@
|
||||||
"webtorrent": "^0.108.1",
|
"webtorrent": "^0.108.1",
|
||||||
"whatwg-fetch": "^3.0.0",
|
"whatwg-fetch": "^3.0.0",
|
||||||
"zone.js": "~0.10.2"
|
"zone.js": "~0.10.2"
|
||||||
},
|
}
|
||||||
"dependencies": {}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,12 +144,7 @@
|
||||||
<button i18n class="reset-button reset-button-small" (click)="resetField('tagsAllOf')" *ngIf="advancedSearch.tagsAllOf">
|
<button i18n class="reset-button reset-button-small" (click)="resetField('tagsAllOf')" *ngIf="advancedSearch.tagsAllOf">
|
||||||
Reset
|
Reset
|
||||||
</button>
|
</button>
|
||||||
<tag-input
|
<my-select-tags labelForId="tagsAllOf" id="tagsAllOf" [(ngModel)]="advancedSearch.tagsAllOf"></my-select-tags>
|
||||||
[(ngModel)]="advancedSearch.tagsAllOf" name="tagsAllOf" id="tagsAllOf"
|
|
||||||
[validators]="tagValidators" [errorMessages]="tagValidatorsMessages"
|
|
||||||
i18n-placeholder placeholder="+ Tag" i18n-secondaryPlaceholder secondaryPlaceholder="Enter a tag"
|
|
||||||
[maxItems]="5" [modelAsStrings]="true"
|
|
||||||
></tag-input>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -157,12 +152,7 @@
|
||||||
<button i18n class="reset-button reset-button-small" (click)="resetField('tagsOneOf')" *ngIf="advancedSearch.tagsOneOf">
|
<button i18n class="reset-button reset-button-small" (click)="resetField('tagsOneOf')" *ngIf="advancedSearch.tagsOneOf">
|
||||||
Reset
|
Reset
|
||||||
</button>
|
</button>
|
||||||
<tag-input
|
<my-select-tags labelForId="tagsOneOf" id="tagsOneOf" [(ngModel)]="advancedSearch.tagsOneOf"></my-select-tags>
|
||||||
[(ngModel)]="advancedSearch.tagsOneOf" name="tagsOneOf" id="tagsOneOf"
|
|
||||||
[validators]="tagValidators" [errorMessages]="tagValidatorsMessages"
|
|
||||||
i18n-placeholder placeholder="+ Tag" i18n-secondaryPlaceholder secondaryPlaceholder="Enter a tag"
|
|
||||||
[maxItems]="5" [modelAsStrings]="true"
|
|
||||||
></tag-input>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group" *ngIf="isSearchTargetEnabled()">
|
<div class="form-group" *ngIf="isSearchTargetEnabled()">
|
||||||
|
|
|
@ -65,5 +65,3 @@ input[type=submit] {
|
||||||
display: flex;
|
display: flex;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include ng2-tags;
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
|
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
|
||||||
import { ValidatorFn } from '@angular/forms'
|
|
||||||
import { ServerService } from '@app/core'
|
import { ServerService } from '@app/core'
|
||||||
import { VideoValidatorsService } from '@app/shared/shared-forms'
|
import { VideoValidatorsService } from '@app/shared/shared-forms'
|
||||||
import { AdvancedSearch } from '@app/shared/shared-search'
|
import { AdvancedSearch } from '@app/shared/shared-search'
|
||||||
|
@ -20,9 +19,6 @@ export class SearchFiltersComponent implements OnInit {
|
||||||
videoLicences: VideoConstant<number>[] = []
|
videoLicences: VideoConstant<number>[] = []
|
||||||
videoLanguages: VideoConstant<string>[] = []
|
videoLanguages: VideoConstant<string>[] = []
|
||||||
|
|
||||||
tagValidators: ValidatorFn[]
|
|
||||||
tagValidatorsMessages: { [ name: string ]: string }
|
|
||||||
|
|
||||||
publishedDateRanges: { id: string, label: string }[] = []
|
publishedDateRanges: { id: string, label: string }[] = []
|
||||||
sorts: { id: string, label: string }[] = []
|
sorts: { id: string, label: string }[] = []
|
||||||
durationRanges: { id: string, label: string }[] = []
|
durationRanges: { id: string, label: string }[] = []
|
||||||
|
@ -40,8 +36,6 @@ export class SearchFiltersComponent implements OnInit {
|
||||||
private videoValidatorsService: VideoValidatorsService,
|
private videoValidatorsService: VideoValidatorsService,
|
||||||
private serverService: ServerService
|
private serverService: ServerService
|
||||||
) {
|
) {
|
||||||
this.tagValidators = this.videoValidatorsService.VIDEO_TAGS.VALIDATORS
|
|
||||||
this.tagValidatorsMessages = this.videoValidatorsService.VIDEO_TAGS.MESSAGES
|
|
||||||
this.publishedDateRanges = [
|
this.publishedDateRanges = [
|
||||||
{
|
{
|
||||||
id: 'any_published_date',
|
id: 'any_published_date',
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { TagInputModule } from 'ngx-chips'
|
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { SharedFormModule } from '@app/shared/shared-forms'
|
import { SharedFormModule } from '@app/shared/shared-forms'
|
||||||
import { SharedMainModule } from '@app/shared/shared-main'
|
import { SharedMainModule } from '@app/shared/shared-main'
|
||||||
|
@ -14,8 +13,6 @@ import { VideoLazyLoadResolver } from './video-lazy-load.resolver'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
TagInputModule,
|
|
||||||
|
|
||||||
SearchRoutingModule,
|
SearchRoutingModule,
|
||||||
|
|
||||||
SharedMainModule,
|
SharedMainModule,
|
||||||
|
@ -31,7 +28,6 @@ import { VideoLazyLoadResolver } from './video-lazy-load.resolver'
|
||||||
],
|
],
|
||||||
|
|
||||||
exports: [
|
exports: [
|
||||||
TagInputModule,
|
|
||||||
SearchComponent
|
SearchComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<label i18n for="language">Language</label>
|
<label i18n for="language">Language</label>
|
||||||
<div class="peertube-select-container">
|
<div class="peertube-ng-select-container">
|
||||||
<select id="language" formControlName="language" class="form-control">
|
<ng-select
|
||||||
<option></option>
|
labelForId="language" [items]="videoCaptionLanguages" formControlName="language"
|
||||||
<option *ngFor="let language of videoCaptionLanguages" [value]="language.id">{{ language.label }}</option>
|
bindLabel="label" bindValue="id"
|
||||||
</select>
|
></ng-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="formErrors.language" class="form-error">
|
<div *ngIf="formErrors.language" class="form-error">
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
@import '_variables';
|
@import '_variables';
|
||||||
@import '_mixins';
|
@import '_mixins';
|
||||||
|
|
||||||
.peertube-select-container {
|
label {
|
||||||
@include peertube-select-container(auto);
|
font-weight: $font-regular;
|
||||||
|
font-size: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.caption-file {
|
.caption-file {
|
||||||
|
|
|
@ -28,11 +28,10 @@
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</my-help>
|
</my-help>
|
||||||
|
|
||||||
<tag-input
|
<my-select-tags labelForId="label-tags" formControlName="tags"></my-select-tags>
|
||||||
[validators]="tagValidators" [errorMessages]="tagValidatorsMessages"
|
<div *ngIf="formErrors.tags" class="form-error">
|
||||||
i18n-placeholder placeholder="+ Tag" i18n-secondaryPlaceholder secondaryPlaceholder="Enter a new tag"
|
{{ formErrors.tags }}
|
||||||
formControlName="tags" [maxItems]="5" [modelAsStrings]="true"
|
</div>
|
||||||
></tag-input>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -56,22 +55,15 @@
|
||||||
|
|
||||||
<div class="col-video-edit">
|
<div class="col-video-edit">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label i18n>Channel</label>
|
<label i18n for="channel">Channel</label>
|
||||||
<div class="peertube-select-container">
|
<my-select-channel labelForId="channel" [items]="userVideoChannels" formControlName="channelId"></my-select-channel>
|
||||||
<select formControlName="channelId" class="form-control">
|
|
||||||
<option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label i18n for="category">Category</label>
|
<label i18n for="category">Category</label>
|
||||||
<div class="peertube-select-container">
|
<my-select-options
|
||||||
<select id="category" formControlName="category" class="form-control">
|
labelForId="category" [items]="videoCategories" formControlName="category" [clearable]="true"
|
||||||
<option></option>
|
></my-select-options>
|
||||||
<option *ngFor="let category of videoCategories" [value]="category.id">{{ category.label }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="formErrors.category" class="form-error">
|
<div *ngIf="formErrors.category" class="form-error">
|
||||||
{{ formErrors.category }}
|
{{ formErrors.category }}
|
||||||
|
@ -80,12 +72,9 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label i18n for="licence">Licence</label>
|
<label i18n for="licence">Licence</label>
|
||||||
<div class="peertube-select-container">
|
<my-select-options
|
||||||
<select id="licence" formControlName="licence" class="form-control">
|
labelForId="licence" [items]="videoLicences" formControlName="licence" [clearable]="true"
|
||||||
<option></option>
|
></my-select-options>
|
||||||
<option *ngFor="let licence of videoLicences" [value]="licence.id">{{ licence.label }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="formErrors.licence" class="form-error">
|
<div *ngIf="formErrors.licence" class="form-error">
|
||||||
{{ formErrors.licence }}
|
{{ formErrors.licence }}
|
||||||
|
@ -94,12 +83,10 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label i18n for="language">Language</label>
|
<label i18n for="language">Language</label>
|
||||||
<div class="peertube-select-container">
|
<my-select-options
|
||||||
<select id="language" formControlName="language" class="form-control">
|
labelForId="language" [items]="videoLanguages" formControlName="language"
|
||||||
<option></option>
|
[clearable]="true" [searchable]="true" [groupBy]="'group'"
|
||||||
<option *ngFor="let language of videoLanguages" [value]="language.id">{{ language.label }}</option>
|
></my-select-options>
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="formErrors.language" class="form-error">
|
<div *ngIf="formErrors.language" class="form-error">
|
||||||
{{ formErrors.language }}
|
{{ formErrors.language }}
|
||||||
|
@ -108,13 +95,9 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label i18n for="privacy">Privacy</label>
|
<label i18n for="privacy">Privacy</label>
|
||||||
<div class="peertube-select-container">
|
<my-select-options
|
||||||
<select id="privacy" formControlName="privacy" class="form-control">
|
labelForId="privacy" [items]="videoPrivacies" formControlName="privacy" [clearable]="false"
|
||||||
<option></option>
|
></my-select-options>
|
||||||
<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">
|
<div *ngIf="formErrors.privacy" class="form-error">
|
||||||
{{ formErrors.privacy }}
|
{{ formErrors.privacy }}
|
||||||
|
@ -136,7 +119,7 @@
|
||||||
|
|
||||||
<my-peertube-checkbox inputName="nsfw" formControlName="nsfw" helpPlacement="bottom-right">
|
<my-peertube-checkbox inputName="nsfw" formControlName="nsfw" helpPlacement="bottom-right">
|
||||||
<ng-template ptTemplate="label">
|
<ng-template ptTemplate="label">
|
||||||
<ng-container i18n>This video contains mature or explicit content</ng-container>
|
<ng-container i18n>Contains sensitive content</ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template ptTemplate="help">
|
<ng-template ptTemplate="help">
|
||||||
|
@ -146,7 +129,7 @@
|
||||||
|
|
||||||
<my-peertube-checkbox *ngIf="waitTranscodingEnabled" inputName="waitTranscoding" formControlName="waitTranscoding" helpPlacement="bottom-right">
|
<my-peertube-checkbox *ngIf="waitTranscodingEnabled" inputName="waitTranscoding" formControlName="waitTranscoding" helpPlacement="bottom-right">
|
||||||
<ng-template ptTemplate="label">
|
<ng-template ptTemplate="label">
|
||||||
<ng-container i18n>Wait transcoding before publishing the video</ng-container>
|
<ng-container i18n>Publish after transcoding</ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template ptTemplate="help">
|
<ng-template ptTemplate="help">
|
||||||
|
|
|
@ -161,8 +161,6 @@ p-calendar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include ng2-tags;
|
|
||||||
|
|
||||||
// columns for the video
|
// columns for the video
|
||||||
.col-video-edit {
|
.col-video-edit {
|
||||||
@include make-col-ready();
|
@include make-col-ready();
|
||||||
|
|
|
@ -8,6 +8,11 @@ import { VideoCaptionEdit, VideoEdit, VideoService } from '@app/shared/shared-ma
|
||||||
import { ServerConfig, VideoConstant, VideoPrivacy } from '@shared/models'
|
import { ServerConfig, VideoConstant, VideoPrivacy } from '@shared/models'
|
||||||
import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service'
|
import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service'
|
||||||
import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component'
|
import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component'
|
||||||
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
|
import { forkJoin } from 'rxjs'
|
||||||
|
import { InstanceService } from '@app/shared/shared-instance'
|
||||||
|
|
||||||
|
type VideoLanguages = VideoConstant<string> & { group?: string }
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-edit',
|
selector: 'my-video-edit',
|
||||||
|
@ -31,7 +36,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
|
||||||
videoPrivacies: VideoConstant<VideoPrivacy>[] = []
|
videoPrivacies: VideoConstant<VideoPrivacy>[] = []
|
||||||
videoCategories: VideoConstant<number>[] = []
|
videoCategories: VideoConstant<number>[] = []
|
||||||
videoLicences: VideoConstant<number>[] = []
|
videoLicences: VideoConstant<number>[] = []
|
||||||
videoLanguages: VideoConstant<string>[] = []
|
videoLanguages: VideoLanguages[] = []
|
||||||
|
|
||||||
tagValidators: ValidatorFn[]
|
tagValidators: ValidatorFn[]
|
||||||
tagValidatorsMessages: { [ name: string ]: string }
|
tagValidatorsMessages: { [ name: string ]: string }
|
||||||
|
@ -56,12 +61,11 @@ export class VideoEditComponent implements OnInit, OnDestroy {
|
||||||
private videoValidatorsService: VideoValidatorsService,
|
private videoValidatorsService: VideoValidatorsService,
|
||||||
private videoService: VideoService,
|
private videoService: VideoService,
|
||||||
private serverService: ServerService,
|
private serverService: ServerService,
|
||||||
|
private instanceService: InstanceService,
|
||||||
private i18nPrimengCalendarService: I18nPrimengCalendarService,
|
private i18nPrimengCalendarService: I18nPrimengCalendarService,
|
||||||
|
private i18n: I18n,
|
||||||
private ngZone: NgZone
|
private ngZone: NgZone
|
||||||
) {
|
) {
|
||||||
this.tagValidators = this.videoValidatorsService.VIDEO_TAGS.VALIDATORS
|
|
||||||
this.tagValidatorsMessages = this.videoValidatorsService.VIDEO_TAGS.MESSAGES
|
|
||||||
|
|
||||||
this.calendarLocale = this.i18nPrimengCalendarService.getCalendarLocale()
|
this.calendarLocale = this.i18nPrimengCalendarService.getCalendarLocale()
|
||||||
this.calendarTimezone = this.i18nPrimengCalendarService.getTimezone()
|
this.calendarTimezone = this.i18nPrimengCalendarService.getTimezone()
|
||||||
this.calendarDateFormat = this.i18nPrimengCalendarService.getDateFormat()
|
this.calendarDateFormat = this.i18nPrimengCalendarService.getDateFormat()
|
||||||
|
@ -93,7 +97,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
|
||||||
licence: this.videoValidatorsService.VIDEO_LICENCE,
|
licence: this.videoValidatorsService.VIDEO_LICENCE,
|
||||||
language: this.videoValidatorsService.VIDEO_LANGUAGE,
|
language: this.videoValidatorsService.VIDEO_LANGUAGE,
|
||||||
description: this.videoValidatorsService.VIDEO_DESCRIPTION,
|
description: this.videoValidatorsService.VIDEO_DESCRIPTION,
|
||||||
tags: null,
|
tags: this.videoValidatorsService.VIDEO_TAGS_ARRAY,
|
||||||
previewfile: null,
|
previewfile: null,
|
||||||
support: this.videoValidatorsService.VIDEO_SUPPORT,
|
support: this.videoValidatorsService.VIDEO_SUPPORT,
|
||||||
schedulePublicationAt: this.videoValidatorsService.VIDEO_SCHEDULE_PUBLICATION_AT,
|
schedulePublicationAt: this.videoValidatorsService.VIDEO_SCHEDULE_PUBLICATION_AT,
|
||||||
|
@ -126,11 +130,29 @@ export class VideoEditComponent implements OnInit, OnDestroy {
|
||||||
.subscribe(res => this.videoCategories = res)
|
.subscribe(res => this.videoCategories = res)
|
||||||
this.serverService.getVideoLicences()
|
this.serverService.getVideoLicences()
|
||||||
.subscribe(res => this.videoLicences = res)
|
.subscribe(res => this.videoLicences = res)
|
||||||
|
forkJoin([
|
||||||
|
this.instanceService.getAbout(),
|
||||||
this.serverService.getVideoLanguages()
|
this.serverService.getVideoLanguages()
|
||||||
.subscribe(res => this.videoLanguages = res)
|
]).pipe(map(([ about, languages ]) => ({ about, languages })))
|
||||||
|
.subscribe(res => {
|
||||||
|
this.videoLanguages = res.languages
|
||||||
|
.map(l => res.about.instance.languages.includes(l.id)
|
||||||
|
? { ...l, group: this.i18n('Instance languages'), groupOrder: 0 }
|
||||||
|
: { ...l, group: this.i18n('All languages'), groupOrder: 1 })
|
||||||
|
.sort((a, b) => a.groupOrder - b.groupOrder)
|
||||||
|
})
|
||||||
|
|
||||||
this.serverService.getVideoPrivacies()
|
this.serverService.getVideoPrivacies()
|
||||||
.subscribe(privacies => this.videoPrivacies = this.videoService.explainedPrivacyLabels(privacies))
|
.subscribe(privacies => {
|
||||||
|
this.videoPrivacies = this.videoService.explainedPrivacyLabels(privacies)
|
||||||
|
if (this.schedulePublicationPossible) {
|
||||||
|
this.videoPrivacies.push({
|
||||||
|
id: this.SPECIAL_SCHEDULED_PRIVACY,
|
||||||
|
label: this.i18n('Scheduled'),
|
||||||
|
description: this.i18n('Hide the video until a specific date')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
this.serverConfig = this.serverService.getTmpConfig()
|
this.serverConfig = this.serverService.getTmpConfig()
|
||||||
this.serverService.getConfig()
|
this.serverService.getConfig()
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { TagInputModule } from 'ngx-chips'
|
|
||||||
import { CalendarModule } from 'primeng/calendar'
|
import { CalendarModule } from 'primeng/calendar'
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { SharedFormModule } from '@app/shared/shared-forms'
|
import { SharedFormModule } from '@app/shared/shared-forms'
|
||||||
|
@ -10,7 +9,6 @@ import { VideoEditComponent } from './video-edit.component'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
TagInputModule,
|
|
||||||
CalendarModule,
|
CalendarModule,
|
||||||
|
|
||||||
SharedMainModule,
|
SharedMainModule,
|
||||||
|
@ -24,7 +22,6 @@ import { VideoEditComponent } from './video-edit.component'
|
||||||
],
|
],
|
||||||
|
|
||||||
exports: [
|
exports: [
|
||||||
TagInputModule,
|
|
||||||
CalendarModule,
|
CalendarModule,
|
||||||
|
|
||||||
SharedMainModule,
|
SharedMainModule,
|
||||||
|
|
|
@ -25,20 +25,16 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label i18n for="first-step-channel">Channel</label>
|
<label i18n for="first-step-channel">Channel</label>
|
||||||
<div class="peertube-select-container">
|
<my-select-channel
|
||||||
<select id="first-step-channel" [(ngModel)]="firstStepChannelId" class="form-control">
|
labelForId="first-step-channel" [items]="userVideoChannels" [(ngModel)]="firstStepChannelId"
|
||||||
<option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
|
></my-select-channel>
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label i18n for="first-step-privacy">Privacy</label>
|
<label i18n for="first-step-privacy">Privacy</label>
|
||||||
<div class="peertube-select-container">
|
<my-select-options
|
||||||
<select id="first-step-privacy" [(ngModel)]="firstStepPrivacyId" class="form-control">
|
labelForId="first-step-privacy" [items]="videoPrivacies" [(ngModel)]="firstStepPrivacyId"
|
||||||
<option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option>
|
></my-select-options>
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -20,20 +20,16 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label i18n for="first-step-channel">Channel</label>
|
<label i18n for="first-step-channel">Channel</label>
|
||||||
<div class="peertube-select-container">
|
<my-select-channel
|
||||||
<select id="first-step-channel" [(ngModel)]="firstStepChannelId" class="form-control">
|
labelForId="first-step-channel" [items]="userVideoChannels" [(ngModel)]="firstStepChannelId"
|
||||||
<option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
|
></my-select-channel>
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label i18n for="first-step-privacy">Privacy</label>
|
<label i18n for="first-step-privacy">Privacy</label>
|
||||||
<div class="peertube-select-container">
|
<my-select-options
|
||||||
<select id="first-step-privacy" [(ngModel)]="firstStepPrivacyId" class="form-control">
|
labelForId="first-step-privacy" [items]="videoPrivacies" [(ngModel)]="firstStepPrivacyId"
|
||||||
<option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option>
|
></my-select-options>
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -26,6 +26,13 @@ $width-size: 190px;
|
||||||
.peertube-select-container {
|
.peertube-select-container {
|
||||||
@include peertube-select-container($width-size);
|
@include peertube-select-container($width-size);
|
||||||
}
|
}
|
||||||
|
my-select-options ::ng-deep ng-select,
|
||||||
|
my-select-channel ::ng-deep ng-select {
|
||||||
|
width: $width-size;
|
||||||
|
@media screen and (max-width: $width-size) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
input[type=text] {
|
input[type=text] {
|
||||||
@include peertube-input-text($width-size);
|
@include peertube-input-text($width-size);
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { ServerConfig, VideoConstant, VideoPrivacy } from '@shared/models'
|
||||||
@Directive()
|
@Directive()
|
||||||
// tslint:disable-next-line: directive-class-suffix
|
// tslint:disable-next-line: directive-class-suffix
|
||||||
export abstract class VideoSend extends FormReactive implements OnInit {
|
export abstract class VideoSend extends FormReactive implements OnInit {
|
||||||
userVideoChannels: { id: number, label: string, support: string }[] = []
|
userVideoChannels: { id: number, label: string, support: string, avatarPath?: string }[] = []
|
||||||
videoPrivacies: VideoConstant<VideoPrivacy>[] = []
|
videoPrivacies: VideoConstant<VideoPrivacy>[] = []
|
||||||
videoCaptions: VideoCaptionEdit[] = []
|
videoCaptions: VideoCaptionEdit[] = []
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ export abstract class VideoSend extends FormReactive implements OnInit {
|
||||||
this.serverService.getVideoPrivacies()
|
this.serverService.getVideoPrivacies()
|
||||||
.subscribe(
|
.subscribe(
|
||||||
privacies => {
|
privacies => {
|
||||||
this.videoPrivacies = privacies
|
this.videoPrivacies = this.videoService.explainedPrivacyLabels(privacies)
|
||||||
|
|
||||||
this.firstStepPrivacyId = this.DEFAULT_VIDEO_PRIVACY
|
this.firstStepPrivacyId = this.DEFAULT_VIDEO_PRIVACY
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,21 +9,16 @@
|
||||||
|
|
||||||
<div class="form-group form-group-channel">
|
<div class="form-group form-group-channel">
|
||||||
<label i18n for="first-step-channel">Channel</label>
|
<label i18n for="first-step-channel">Channel</label>
|
||||||
<div class="peertube-select-container">
|
<my-select-channel
|
||||||
<select id="first-step-channel" [(ngModel)]="firstStepChannelId" class="form-control">
|
labelForId="first-step-channel" [items]="userVideoChannels" [(ngModel)]="firstStepChannelId"
|
||||||
<option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
|
></my-select-channel>
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label i18n for="first-step-privacy">Privacy</label>
|
<label i18n for="first-step-privacy">Privacy</label>
|
||||||
<div class="peertube-select-container">
|
<my-select-options
|
||||||
<select id="first-step-privacy" [(ngModel)]="firstStepPrivacyId" class="form-control">
|
labelForId="first-step-privacy" [items]="videoPrivacies" [(ngModel)]="firstStepPrivacyId"
|
||||||
<option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option>
|
></my-select-options>
|
||||||
<option i18n [value]="SPECIAL_SCHEDULED_PRIVACY">Scheduled</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-container *ngIf="isUploadingAudioFile">
|
<ng-container *ngIf="isUploadingAudioFile">
|
||||||
|
|
|
@ -17,7 +17,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
|
||||||
video: VideoEdit
|
video: VideoEdit
|
||||||
|
|
||||||
isUpdatingVideo = false
|
isUpdatingVideo = false
|
||||||
userVideoChannels: { id: number, label: string, support: string }[] = []
|
userVideoChannels: { id: number, label: string, support: string, avatar?: string }[] = []
|
||||||
schedulePublicationPossible = false
|
schedulePublicationPossible = false
|
||||||
videoCaptions: VideoCaptionEdit[] = []
|
videoCaptions: VideoCaptionEdit[] = []
|
||||||
waitTranscodingEnabled = true
|
waitTranscodingEnabled = true
|
||||||
|
|
|
@ -28,7 +28,12 @@ export class VideoUpdateResolver implements Resolve<any> {
|
||||||
.listAccountVideoChannels(video.account)
|
.listAccountVideoChannels(video.account)
|
||||||
.pipe(
|
.pipe(
|
||||||
map(result => result.data),
|
map(result => result.data),
|
||||||
map(videoChannels => videoChannels.map(c => ({ id: c.id, label: c.displayName, support: c.support })))
|
map(videoChannels => videoChannels.map(c => ({
|
||||||
|
id: c.id,
|
||||||
|
label: c.displayName,
|
||||||
|
support: c.support,
|
||||||
|
avatarPath: c.avatar?.path
|
||||||
|
})))
|
||||||
),
|
),
|
||||||
|
|
||||||
this.videoCaptionService
|
this.videoCaptionService
|
||||||
|
|
|
@ -16,7 +16,10 @@ function getParameterByName (name: string, url: string) {
|
||||||
return decodeURIComponent(results[2].replace(/\+/g, ' '))
|
return decodeURIComponent(results[2].replace(/\+/g, ' '))
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateAsyncUserVideoChannels (authService: AuthService, channel: { id: number, label: string, support?: string }[]) {
|
function populateAsyncUserVideoChannels (
|
||||||
|
authService: AuthService,
|
||||||
|
channel: { id: number, label: string, support?: string, avatarPath?: string, recent?: boolean }[]
|
||||||
|
) {
|
||||||
return new Promise(res => {
|
return new Promise(res => {
|
||||||
authService.userInformationLoaded
|
authService.userInformationLoaded
|
||||||
.subscribe(
|
.subscribe(
|
||||||
|
@ -27,7 +30,12 @@ function populateAsyncUserVideoChannels (authService: AuthService, channel: { id
|
||||||
const videoChannels = user.videoChannels
|
const videoChannels = user.videoChannels
|
||||||
if (Array.isArray(videoChannels) === false) return
|
if (Array.isArray(videoChannels) === false) return
|
||||||
|
|
||||||
videoChannels.forEach(c => channel.push({ id: c.id, label: c.displayName, support: c.support }))
|
videoChannels.forEach(c => channel.push({
|
||||||
|
id: c.id,
|
||||||
|
label: c.displayName,
|
||||||
|
support: c.support,
|
||||||
|
avatarPath: c.avatar?.path
|
||||||
|
}))
|
||||||
|
|
||||||
return res()
|
return res()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { Validators } from '@angular/forms'
|
import { Validators, ValidatorFn, ValidationErrors, AbstractControl } from '@angular/forms'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { BuildFormValidator } from './form-validator.service'
|
import { BuildFormValidator } from './form-validator.service'
|
||||||
|
|
||||||
|
@ -13,7 +13,8 @@ export class VideoValidatorsService {
|
||||||
readonly VIDEO_IMAGE: BuildFormValidator
|
readonly VIDEO_IMAGE: BuildFormValidator
|
||||||
readonly VIDEO_CHANNEL: BuildFormValidator
|
readonly VIDEO_CHANNEL: BuildFormValidator
|
||||||
readonly VIDEO_DESCRIPTION: BuildFormValidator
|
readonly VIDEO_DESCRIPTION: BuildFormValidator
|
||||||
readonly VIDEO_TAGS: BuildFormValidator
|
readonly VIDEO_TAGS_ARRAY: BuildFormValidator
|
||||||
|
readonly VIDEO_TAG: BuildFormValidator
|
||||||
readonly VIDEO_SUPPORT: BuildFormValidator
|
readonly VIDEO_SUPPORT: BuildFormValidator
|
||||||
readonly VIDEO_SCHEDULE_PUBLICATION_AT: BuildFormValidator
|
readonly VIDEO_SCHEDULE_PUBLICATION_AT: BuildFormValidator
|
||||||
readonly VIDEO_ORIGINALLY_PUBLISHED_AT: BuildFormValidator
|
readonly VIDEO_ORIGINALLY_PUBLISHED_AT: BuildFormValidator
|
||||||
|
@ -71,7 +72,7 @@ export class VideoValidatorsService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.VIDEO_TAGS = {
|
this.VIDEO_TAG = {
|
||||||
VALIDATORS: [ Validators.minLength(2), Validators.maxLength(30) ],
|
VALIDATORS: [ Validators.minLength(2), Validators.maxLength(30) ],
|
||||||
MESSAGES: {
|
MESSAGES: {
|
||||||
'minlength': this.i18n('A tag should be more than 2 characters long.'),
|
'minlength': this.i18n('A tag should be more than 2 characters long.'),
|
||||||
|
@ -79,6 +80,14 @@ export class VideoValidatorsService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.VIDEO_TAGS_ARRAY = {
|
||||||
|
VALIDATORS: [ Validators.maxLength(5), this.arrayTagLengthValidator() ],
|
||||||
|
MESSAGES: {
|
||||||
|
'maxlength': this.i18n('A maximum of 5 tags can be used on a video.'),
|
||||||
|
'arrayTagLength': this.i18n('A tag should be more than 2, and less than 30 characters long.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.VIDEO_SUPPORT = {
|
this.VIDEO_SUPPORT = {
|
||||||
VALIDATORS: [ Validators.minLength(3), Validators.maxLength(1000) ],
|
VALIDATORS: [ Validators.minLength(3), Validators.maxLength(1000) ],
|
||||||
MESSAGES: {
|
MESSAGES: {
|
||||||
|
@ -99,4 +108,16 @@ export class VideoValidatorsService {
|
||||||
MESSAGES: {}
|
MESSAGES: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arrayTagLengthValidator (min = 2, max = 30): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors => {
|
||||||
|
const array = control.value as Array<string>
|
||||||
|
|
||||||
|
if (array.every(e => e.length > min && e.length < max)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return { 'arrayTagLength': true }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<ng-select
|
||||||
|
[(ngModel)]="selectedId"
|
||||||
|
(ngModelChange)="onModelChange()"
|
||||||
|
[bindLabel]="bindLabel"
|
||||||
|
[bindValue]="bindValue"
|
||||||
|
[clearable]="clearable"
|
||||||
|
[searchable]="searchable"
|
||||||
|
>
|
||||||
|
<ng-option *ngFor="let channel of channels" [value]="channel.id">
|
||||||
|
<img
|
||||||
|
class="avatar mr-1"
|
||||||
|
[src]="channel.avatarPath"
|
||||||
|
/>
|
||||||
|
{{ channel.label }}
|
||||||
|
</ng-option>
|
||||||
|
</ng-select>
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { Component, Input, forwardRef, ViewChild } from '@angular/core'
|
||||||
|
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'
|
||||||
|
import { Actor } from '../shared-main'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-select-channel',
|
||||||
|
styleUrls: [ './select-shared.component.scss' ],
|
||||||
|
templateUrl: './select-channel.component.html',
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => SelectChannelComponent),
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class SelectChannelComponent implements ControlValueAccessor {
|
||||||
|
@Input() items: { id: number, label: string, support: string, avatarPath?: string }[] = []
|
||||||
|
|
||||||
|
selectedId: number
|
||||||
|
|
||||||
|
// ng-select options
|
||||||
|
bindLabel = 'label'
|
||||||
|
bindValue = 'id'
|
||||||
|
clearable = false
|
||||||
|
searchable = false
|
||||||
|
|
||||||
|
get channels () {
|
||||||
|
return this.items.map(c => Object.assign(c, {
|
||||||
|
avatarPath: c.avatarPath ? c.avatarPath : Actor.GET_DEFAULT_AVATAR_URL()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
propagateChange = (_: any) => { /* empty */ }
|
||||||
|
|
||||||
|
writeValue (id: number) {
|
||||||
|
this.selectedId = id
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange (fn: (_: any) => void) {
|
||||||
|
this.propagateChange = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched () {
|
||||||
|
// Unused
|
||||||
|
}
|
||||||
|
|
||||||
|
onModelChange () {
|
||||||
|
this.propagateChange(this.selectedId)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<ng-select
|
||||||
|
[items]="items"
|
||||||
|
[groupBy]="groupBy"
|
||||||
|
[(ngModel)]="selectedId"
|
||||||
|
(ngModelChange)="onModelChange()"
|
||||||
|
[bindLabel]="bindLabel"
|
||||||
|
[bindValue]="bindValue"
|
||||||
|
[clearable]="clearable"
|
||||||
|
[searchable]="searchable"
|
||||||
|
>
|
||||||
|
<ng-template ng-option-tmp let-item="item" let-index="index">
|
||||||
|
{{ item.label }}
|
||||||
|
<ng-container *ngIf="item.description">
|
||||||
|
<br>
|
||||||
|
<span [title]="item.description" class="text-muted">{{ item.description }}</span>
|
||||||
|
</ng-container>
|
||||||
|
</ng-template>
|
||||||
|
</ng-select>
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { Component, Input, forwardRef } from '@angular/core'
|
||||||
|
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'
|
||||||
|
|
||||||
|
export type SelectOptionsItem = { id: number | string, label: string, description?: string }
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-select-options',
|
||||||
|
styleUrls: [ './select-shared.component.scss' ],
|
||||||
|
templateUrl: './select-options.component.html',
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => SelectOptionsComponent),
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class SelectOptionsComponent implements ControlValueAccessor {
|
||||||
|
@Input() items: SelectOptionsItem[] = []
|
||||||
|
@Input() clearable = false
|
||||||
|
@Input() searchable = false
|
||||||
|
@Input() bindValue = 'id'
|
||||||
|
@Input() groupBy: string
|
||||||
|
|
||||||
|
selectedId: number | string
|
||||||
|
|
||||||
|
// ng-select options
|
||||||
|
bindLabel = 'label'
|
||||||
|
|
||||||
|
propagateChange = (_: any) => { /* empty */ }
|
||||||
|
|
||||||
|
writeValue (id: number | string) {
|
||||||
|
this.selectedId = id
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange (fn: (_: any) => void) {
|
||||||
|
this.propagateChange = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched () {
|
||||||
|
// Unused
|
||||||
|
}
|
||||||
|
|
||||||
|
onModelChange () {
|
||||||
|
this.propagateChange(this.selectedId)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
$width-size: auto;
|
||||||
|
|
||||||
|
ng-select {
|
||||||
|
width: $width-size;
|
||||||
|
@media screen and (max-width: $width-size) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the image is vertically adjusted
|
||||||
|
ng-select ::ng-deep .ng-value-label img {
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ng-select ::ng-deep img {
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<ng-select
|
||||||
|
[items]="items"
|
||||||
|
[(ngModel)]="_items"
|
||||||
|
(ngModelChange)="onModelChange()"
|
||||||
|
i18n-placeholder placeholder="Enter a new tag"
|
||||||
|
[maxSelectedItems]="5"
|
||||||
|
[clearable]="true"
|
||||||
|
[addTag]="true"
|
||||||
|
[multiple]="true"
|
||||||
|
[isOpen]="false"
|
||||||
|
[searchable]="true"
|
||||||
|
>
|
||||||
|
</ng-select>
|
|
@ -0,0 +1,3 @@
|
||||||
|
ng-select ::ng-deep .ng-arrow-wrapper {
|
||||||
|
display: none;
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { Component, Input, forwardRef } from '@angular/core'
|
||||||
|
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-select-tags',
|
||||||
|
styleUrls: [ './select-shared.component.scss', './select-tags.component.scss' ],
|
||||||
|
templateUrl: './select-tags.component.html',
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => SelectTagsComponent),
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class SelectTagsComponent implements ControlValueAccessor {
|
||||||
|
@Input() items: string[] = []
|
||||||
|
@Input() _items: string[] = []
|
||||||
|
|
||||||
|
propagateChange = (_: any) => { /* empty */ }
|
||||||
|
|
||||||
|
writeValue (items: string[]) {
|
||||||
|
this._items = items
|
||||||
|
this.propagateChange(this._items)
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange (fn: (_: any) => void) {
|
||||||
|
this.propagateChange = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched () {
|
||||||
|
// Unused
|
||||||
|
}
|
||||||
|
|
||||||
|
onModelChange () {
|
||||||
|
this.propagateChange(this._items)
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,9 @@ import { PreviewUploadComponent } from './preview-upload.component'
|
||||||
import { ReactiveFileComponent } from './reactive-file.component'
|
import { ReactiveFileComponent } from './reactive-file.component'
|
||||||
import { TextareaAutoResizeDirective } from './textarea-autoresize.directive'
|
import { TextareaAutoResizeDirective } from './textarea-autoresize.directive'
|
||||||
import { TimestampInputComponent } from './timestamp-input.component'
|
import { TimestampInputComponent } from './timestamp-input.component'
|
||||||
|
import { SelectChannelComponent } from './select-channel.component'
|
||||||
|
import { SelectOptionsComponent } from './select-options.component'
|
||||||
|
import { SelectTagsComponent } from './select-tags.component'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -45,7 +48,10 @@ import { TimestampInputComponent } from './timestamp-input.component'
|
||||||
PreviewUploadComponent,
|
PreviewUploadComponent,
|
||||||
ReactiveFileComponent,
|
ReactiveFileComponent,
|
||||||
TextareaAutoResizeDirective,
|
TextareaAutoResizeDirective,
|
||||||
TimestampInputComponent
|
TimestampInputComponent,
|
||||||
|
SelectChannelComponent,
|
||||||
|
SelectOptionsComponent,
|
||||||
|
SelectTagsComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
exports: [
|
exports: [
|
||||||
|
@ -58,7 +64,10 @@ import { TimestampInputComponent } from './timestamp-input.component'
|
||||||
PreviewUploadComponent,
|
PreviewUploadComponent,
|
||||||
ReactiveFileComponent,
|
ReactiveFileComponent,
|
||||||
TextareaAutoResizeDirective,
|
TextareaAutoResizeDirective,
|
||||||
TimestampInputComponent
|
TimestampInputComponent,
|
||||||
|
SelectChannelComponent,
|
||||||
|
SelectOptionsComponent,
|
||||||
|
SelectTagsComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
providers: [
|
providers: [
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
NgbPopoverModule,
|
NgbPopoverModule,
|
||||||
NgbTooltipModule
|
NgbTooltipModule
|
||||||
} from '@ng-bootstrap/ng-bootstrap'
|
} from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { NgSelectModule } from '@ng-select/ng-select'
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { SharedGlobalIconModule } from '../shared-icons'
|
import { SharedGlobalIconModule } from '../shared-icons'
|
||||||
import { AccountService, ActorAvatarInfoComponent, AvatarComponent } from './account'
|
import { AccountService, ActorAvatarInfoComponent, AvatarComponent } from './account'
|
||||||
|
@ -55,6 +56,8 @@ import { AUTH_INTERCEPTOR_PROVIDER } from './auth'
|
||||||
MultiSelectModule,
|
MultiSelectModule,
|
||||||
InputSwitchModule,
|
InputSwitchModule,
|
||||||
|
|
||||||
|
NgSelectModule,
|
||||||
|
|
||||||
SharedGlobalIconModule
|
SharedGlobalIconModule
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -134,7 +137,9 @@ import { AUTH_INTERCEPTOR_PROVIDER } from './auth'
|
||||||
TopMenuDropdownComponent,
|
TopMenuDropdownComponent,
|
||||||
|
|
||||||
UserQuotaComponent,
|
UserQuotaComponent,
|
||||||
UserNotificationsComponent
|
UserNotificationsComponent,
|
||||||
|
|
||||||
|
NgSelectModule
|
||||||
],
|
],
|
||||||
|
|
||||||
providers: [
|
providers: [
|
||||||
|
|
|
@ -339,23 +339,25 @@ export class VideoService implements VideosProvider {
|
||||||
const base = [
|
const base = [
|
||||||
{
|
{
|
||||||
id: VideoPrivacy.PRIVATE,
|
id: VideoPrivacy.PRIVATE,
|
||||||
label: this.i18n('Only I can see this video')
|
description: this.i18n('Only I can see this video')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: VideoPrivacy.UNLISTED,
|
id: VideoPrivacy.UNLISTED,
|
||||||
label: this.i18n('Only people with the private link can see this video')
|
description: this.i18n('Only shareable via a private link')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: VideoPrivacy.PUBLIC,
|
id: VideoPrivacy.PUBLIC,
|
||||||
label: this.i18n('Anyone can see this video')
|
description: this.i18n('Anyone can see this video')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: VideoPrivacy.INTERNAL,
|
id: VideoPrivacy.INTERNAL,
|
||||||
label: this.i18n('Only users of this instance can see this video')
|
description: this.i18n('Only users of this instance can see this video')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
return base.filter(o => !!privacies.find(p => p.id === o.id))
|
return base
|
||||||
|
.filter(o => !!privacies.find(p => p.id === o.id)) // filter down to privacies that where in the input
|
||||||
|
.map(o => ({ ...privacies[o.id - 1], ...o })) // merge the input privacies that contain a label, and extend them with a description
|
||||||
}
|
}
|
||||||
|
|
||||||
nsfwPolicyToParam (nsfwPolicy: NSFWPolicyType) {
|
nsfwPolicyToParam (nsfwPolicy: NSFWPolicyType) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ $assets-path: '../../assets/';
|
||||||
|
|
||||||
@import './bootstrap';
|
@import './bootstrap';
|
||||||
@import './primeng-custom';
|
@import './primeng-custom';
|
||||||
|
@import './ng-select.scss';
|
||||||
|
|
||||||
[hidden] {
|
[hidden] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
|
|
|
@ -807,75 +807,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin ng2-tags {
|
|
||||||
::ng-deep {
|
|
||||||
.ng2-tag-input {
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ng2-tags-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
border: 1px solid #C6C6C6;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 5px !important;
|
|
||||||
height: max-content;
|
|
||||||
|
|
||||||
&:focus-within {
|
|
||||||
box-shadow: #{$focus-box-shadow-form} pvar(--mainColorLightest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tag-input-form {
|
|
||||||
input {
|
|
||||||
height: 30px !important;
|
|
||||||
font-size: 12px !important;
|
|
||||||
|
|
||||||
background-color: pvar(--mainBackgroundColor) !important;
|
|
||||||
color: pvar(--mainForegroundColor) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tag {
|
|
||||||
background-color: $grey-background-color !important;
|
|
||||||
color: #000 !important;
|
|
||||||
border-radius: 3px !important;
|
|
||||||
font-size: 12px !important;
|
|
||||||
height: 30px !important;
|
|
||||||
line-height: 30px !important;
|
|
||||||
margin: 0 5px 0 0 !important;
|
|
||||||
cursor: default !important;
|
|
||||||
padding: 0 8px 0 10px !important;
|
|
||||||
|
|
||||||
div {
|
|
||||||
height: 100% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete-icon {
|
|
||||||
cursor: pointer !important;
|
|
||||||
height: auto !important;
|
|
||||||
vertical-align: middle !important;
|
|
||||||
padding-left: 6px !important;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
position: relative;
|
|
||||||
top: -1px;
|
|
||||||
height: auto !important;
|
|
||||||
vertical-align: middle !important;
|
|
||||||
|
|
||||||
path {
|
|
||||||
fill: pvar(--greyForegroundColor) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin divider($color: pvar(--submenuColor), $background: pvar(--mainBackgroundColor)) {
|
@mixin divider($color: pvar(--submenuColor), $background: pvar(--mainBackgroundColor)) {
|
||||||
width: 95%;
|
width: 95%;
|
||||||
border-top: .05rem solid $color;
|
border-top: .05rem solid $color;
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
@import '_variables';
|
||||||
|
|
||||||
|
$ng-select-highlight: #f2690d;
|
||||||
|
// $ng-select-primary-text: #333 !default;
|
||||||
|
// $ng-select-disabled-text: #f9f9f9 !default;
|
||||||
|
// $ng-select-border: #ccc !default;
|
||||||
|
// $ng-select-border-radius: 4px !default;
|
||||||
|
// $ng-select-bg: #ffffff !default;
|
||||||
|
// $ng-select-selected: lighten($ng-select-highlight, 46) !default;
|
||||||
|
// $ng-select-marke d: lighten($ng-select-highlight, 48) !default;
|
||||||
|
$ng-select-box-shadow: #{$focus-box-shadow-form} pvar(--mainColorLightest);
|
||||||
|
// $ng-select-placeholder: lighten($ng-select-primary-text, 40) !default;
|
||||||
|
$ng-select-height: 30px;
|
||||||
|
// $ng-select-value-padding-left: 10px !default;
|
||||||
|
// $ng-select-value-font-size: 0.9em !default;
|
||||||
|
|
||||||
|
@import "~@ng-select/ng-select/scss/default.theme.scss";
|
||||||
|
|
||||||
|
.ng-input {
|
||||||
|
font-size: .9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ng-select {
|
||||||
|
&.ng-select-focused {
|
||||||
|
&:not(.ng-select-opened) > .ng-select-container {
|
||||||
|
border-color: #ccc !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1344,6 +1344,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.0.0"
|
tslib "^2.0.0"
|
||||||
|
|
||||||
|
"@ng-select/ng-select@^5.0.0":
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@ng-select/ng-select/-/ng-select-5.0.0.tgz#db021e5ca7be3fe6be346c3b63b3f2e109afa0f0"
|
||||||
|
integrity sha512-1mdGNh5xGriSnCLcLF/GWxqCgCehB3Stu7mxhB9K/+BxNCRE365Q1MgpzO61XQBgL0L6ktxiXRhCQWXXtFoq9w==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
"@ngtools/webpack@10.1.0-next.4":
|
"@ngtools/webpack@10.1.0-next.4":
|
||||||
version "10.1.0-next.4"
|
version "10.1.0-next.4"
|
||||||
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.1.0-next.4.tgz#5397417820d110e29c7dc99db8adb538de98676e"
|
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.1.0-next.4.tgz#5397417820d110e29c7dc99db8adb538de98676e"
|
||||||
|
@ -7866,21 +7873,6 @@ next-tick@~1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
|
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
|
||||||
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
|
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
|
||||||
|
|
||||||
ng2-material-dropdown@0.11.0:
|
|
||||||
version "0.11.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/ng2-material-dropdown/-/ng2-material-dropdown-0.11.0.tgz#27a402ef3cbdcaf6791ef4cfd4b257e31db7546f"
|
|
||||||
integrity sha512-wptBo09qKecY0QPTProAThrc4A3ajJTcHE9LTpCG5XZZUhXLBzhnGK8OW33TN8A+K/jqcs7OB74ppYJiqs3nhQ==
|
|
||||||
dependencies:
|
|
||||||
tslib "^1.9.0"
|
|
||||||
|
|
||||||
ngx-chips@2.1.0:
|
|
||||||
version "2.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/ngx-chips/-/ngx-chips-2.1.0.tgz#aa299bcf40dc3e1f6288bf1d29e2fdfe9a132ed3"
|
|
||||||
integrity sha512-OQV4dTfD3nXm5d2mGKUSgwOtJOaMnZ4F+lwXOtd7DWRSUne0JQWwoZNHdOpuS6saBGhqCPDAwq6KxdR5XSgZUQ==
|
|
||||||
dependencies:
|
|
||||||
ng2-material-dropdown "0.11.0"
|
|
||||||
tslib "^1.9.0"
|
|
||||||
|
|
||||||
ngx-pipes@^2.6.0:
|
ngx-pipes@^2.6.0:
|
||||||
version "2.7.5"
|
version "2.7.5"
|
||||||
resolved "https://registry.yarnpkg.com/ngx-pipes/-/ngx-pipes-2.7.5.tgz#22e2e4b7015ae9103210dfa2dacd6f4ae4411639"
|
resolved "https://registry.yarnpkg.com/ngx-pipes/-/ngx-pipes-2.7.5.tgz#22e2e4b7015ae9103210dfa2dacd6f4ae4411639"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export interface VideoConstant<T> {
|
export interface VideoConstant<T> {
|
||||||
id: T
|
id: T
|
||||||
label: string
|
label: string
|
||||||
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue