<label i18n for="instanceCategories">Main instance categories</label>
<div>
- <p-multiSelect
- inputId="instanceCategories" [options]="categoryItems" formControlName="categories" [showToggleAll]="false"
- [defaultLabel]="getDefaultCategoryLabel()" [selectedItemsLabel]="getSelectedCategoryLabel()"
- emptyFilterMessage="No results found" i18n-emptyFilterMessage
- ></p-multiSelect>
+ <my-select-checkbox
+ id="instanceCategories"
+ formControlName="categories" [availableItems]="categoryItems"
+ [selectableGroup]="false"
+ >
+ </my-select-checkbox>
</div>
</div>
<label i18n for="instanceLanguages">Main languages you/your moderators speak</label>
<div>
- <p-multiSelect
- inputId="instanceLanguages" [options]="languageItems" formControlName="languages" [showToggleAll]="false"
- [defaultLabel]="getDefaultLanguageLabel()" [selectedItemsLabel]="getSelectedLanguageLabel()"
- emptyFilterMessage="No results found" i18n-emptyFilterMessage
- ></p-multiSelect>
+ <my-select-checkbox
+ id="instanceLanguages"
+ formControlName="languages" [availableItems]="languageItems"
+ [selectableGroup]="false"
+ >
+ </my-select-checkbox>
</div>
</div>
@include peertube-select-container($form-base-input-width);
}
+my-select-checkbox {
+ @include ng-select($form-base-input-width);
+}
+
input[type=submit] {
@include peertube-button;
@include orange-button;
-import { SelectItem } from 'primeng/api'
import { forkJoin } from 'rxjs'
import { ViewportScroller } from '@angular/common'
import { AfterViewChecked, Component, OnInit, ViewChild } from '@angular/core'
import { ConfigService } from '@app/+admin/config/shared/config.service'
import { Notifier } from '@app/core'
import { ServerService } from '@app/core/server/server.service'
-import { CustomConfigValidatorsService, FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms'
+import {
+ CustomConfigValidatorsService,
+ FormReactive,
+ FormValidatorService,
+ SelectOptionsItem,
+ UserValidatorsService
+} from '@app/shared/shared-forms'
import { NgbNav } from '@ng-bootstrap/ng-bootstrap'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { CustomConfig, ServerConfig } from '@shared/models'
resolutions: { id: string, label: string, description?: string }[] = []
transcodingThreadOptions: { label: string, value: number }[] = []
- languageItems: SelectItem[] = []
- categoryItems: SelectItem[] = []
+ languageItems: SelectOptionsItem[] = []
+ categoryItems: SelectOptionsItem[] = []
private serverConfig: ServerConfig
)
}
- getSelectedLanguageLabel () {
- return this.i18n('{{\'{0} languages selected')
- }
-
- getDefaultLanguageLabel () {
- return this.i18n('No language')
- }
-
- getSelectedCategoryLabel () {
- return this.i18n('{{\'{0} categories selected')
- }
-
- getDefaultCategoryLabel () {
- return this.i18n('No category')
- }
-
gotoAnchor () {
const hashToNav = {
'customizations': 'advanced-configuration'
([ config, languages, categories ]) => {
this.customConfig = config
- this.languageItems = languages.map(l => ({ label: l.label, value: l.id }))
- this.categoryItems = categories.map(l => ({ label: l.label, value: l.id }))
+ this.languageItems = languages.map(l => ({ label: l.label, id: l.id }))
+ this.categoryItems = categories.map(l => ({ label: l.label, id: l.id + '' }))
this.updateForm()
// Force form validation
<div role="menu" class="dropdown-menu" ngbDropdownMenu>
<div class="dropdown-header" i18n>Table parameters</div>
<div ngbDropdownItem class="dropdown-item">
- <p-multiSelect
- [options]="columns" [showToggleAll]="true" [(ngModel)]="selectedColumns" optionLabel="label"
- emptyFilterMessage="No matching column found" i18n-emptyFilterMessage [filter]="false"
- selectedItemsLabel="{0} columns displayed" i18n-emptyFilterMessage [showHeader]="false"
- [maxSelectedLabels]="4"
- ></p-multiSelect>
+ <my-select-checkbox
+ name="columns"
+ [availableItems]="columns"
+ [selectableGroup]="false" [(ngModel)]="selectedColumns"
+ >
+ </my-select-checkbox>
</div>
<div ngbDropdownItem class="dropdown-item">
<my-peertube-checkbox inputName="highlightBannedUsers" [(ngModel)]="highlightBannedUsers"
</div>
</div>
</th>
- <th *ngIf="getColumn('username')" pResizableColumn i18n pSortableColumn="username">{{ getColumn('username').label }} <p-sortIcon field="username"></p-sortIcon></th>
- <th *ngIf="getColumn('email')" i18n>{{ getColumn('email').label }}</th>
- <th *ngIf="getColumn('quota')" style="width: 160px;" i18n pSortableColumn="videoQuotaUsed">{{ getColumn('quota').label }} <p-sortIcon field="videoQuotaUsed"></p-sortIcon></th>
- <th *ngIf="getColumn('quotaDaily')" style="width: 160px;" i18n>{{ getColumn('quotaDaily').label }}</th>
- <th *ngIf="getColumn('role')" style="width: 120px;" i18n pSortableColumn="role">{{ getColumn('role').label }} <p-sortIcon field="role"></p-sortIcon></th>
- <th *ngIf="getColumn('pluginAuth')" style="width: 140px;" pResizableColumn i18n>{{ getColumn('pluginAuth').label }}</th>
- <th *ngIf="getColumn('createdAt')" style="width: 150px;" i18n pSortableColumn="createdAt">{{ getColumn('createdAt').label }} <p-sortIcon field="createdAt"></p-sortIcon></th>
- <th *ngIf="getColumn('lastLoginDate')" style="width: 150px;" i18n pSortableColumn="lastLoginDate">{{ getColumn('lastLoginDate').label }} <p-sortIcon field="lastLoginDate"></p-sortIcon></th>
+ <th *ngIf="isSelected('username')" pResizableColumn i18n pSortableColumn="username">{{ getColumn('username').label }} <p-sortIcon field="username"></p-sortIcon></th>
+ <th *ngIf="isSelected('email')" i18n>{{ getColumn('email').label }}</th>
+ <th *ngIf="isSelected('quota')" style="width: 160px;" i18n pSortableColumn="videoQuotaUsed">{{ getColumn('quota').label }} <p-sortIcon field="videoQuotaUsed"></p-sortIcon></th>
+ <th *ngIf="isSelected('quotaDaily')" style="width: 160px;" i18n>{{ getColumn('quotaDaily').label }}</th>
+ <th *ngIf="isSelected('role')" style="width: 120px;" i18n pSortableColumn="role">{{ getColumn('role').label }} <p-sortIcon field="role"></p-sortIcon></th>
+ <th *ngIf="isSelected('pluginAuth')" style="width: 140px;" pResizableColumn i18n>{{ getColumn('pluginAuth').label }}</th>
+ <th *ngIf="isSelected('createdAt')" style="width: 150px;" i18n pSortableColumn="createdAt">{{ getColumn('createdAt').label }} <p-sortIcon field="createdAt"></p-sortIcon></th>
+ <th *ngIf="isSelected('lastLoginDate')" style="width: 150px;" i18n pSortableColumn="lastLoginDate">{{ getColumn('lastLoginDate').label }} <p-sortIcon field="lastLoginDate"></p-sortIcon></th>
</tr>
</ng-template>
</my-user-moderation-dropdown>
</td>
- <td *ngIf="getColumn('username')">
+ <td *ngIf="isSelected('username')">
<a i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + user.username ]">
<div class="chip two-lines">
<img
</a>
</td>
- <td *ngIf="getColumn('email')" [title]="user.email">
+ <td *ngIf="isSelected('email')" [title]="user.email">
<ng-container *ngIf="!requiresEmailVerification || user.blocked; else emailWithVerificationStatus">
<a class="table-email" [href]="'mailto:' + user.email">{{ user.email }}</a>
</ng-container>
</ng-template>
</ng-template>
- <td *ngIf="getColumn('quota')">
+ <td *ngIf="isSelected('quota')">
<div class="progress" i18n-title title="Total video quota">
<div class="progress-bar" role="progressbar" [style]="{ width: getUserVideoQuotaPercentage(user) + '%' }"
[attr.aria-valuenow]="user.rawVideoQuotaUsed" aria-valuemin="0" [attr.aria-valuemax]="user.rawVideoQuota">
</div>
</td>
- <td *ngIf="getColumn('quotaDaily')">
+ <td *ngIf="isSelected('quotaDaily')">
<div class="progress" i18n-title title="Total daily video quota">
<div class="progress-bar secondary" role="progressbar" [style]="{ width: getUserVideoQuotaDailyPercentage(user) + '%' }"
[attr.aria-valuenow]="user.rawVideoQuotaUsedDaily" aria-valuemin="0" [attr.aria-valuemax]="user.rawVideoQuotaDaily">
</div>
</td>
- <td *ngIf="getColumn('role')">
+ <td *ngIf="isSelected('role')">
<span *ngIf="user.blocked" class="badge badge-banned" i18n-title title="The user was banned">{{ user.roleLabel }}</span>
<span *ngIf="!user.blocked" class="badge" [ngClass]="getRoleClass(user.role)">{{ user.roleLabel }}</span>
</td>
- <td *ngIf="getColumn('pluginAuth')">
+ <td *ngIf="isSelected('pluginAuth')">
<ng-container *ngIf="user.pluginAuth">{{ user.pluginAuth }}</ng-container>
</td>
- <td *ngIf="getColumn('createdAt')" [title]="user.createdAt">{{ user.createdAt | date: 'short' }}</td>
+ <td *ngIf="isSelected('createdAt')" [title]="user.createdAt">{{ user.createdAt | date: 'short' }}</td>
- <td *ngIf="getColumn('lastLoginDate')" [title]="user.lastLoginDate">{{ user.lastLoginDate | date: 'short' }}</td>
+ <td *ngIf="isSelected('lastLoginDate')" [title]="user.lastLoginDate">{{ user.lastLoginDate | date: 'short' }}</td>
</tr>
</ng-template>
.input-group {
@include peertube-input-group(300px);
+
input {
flex: 1;
}
selectedUsers: User[] = []
bulkUserActions: DropdownAction<User[]>[][] = []
- columns: { key: string, label: string }[]
+ columns: { id: string, label: string }[]
- private _selectedColumns: { key: string, label: string }[]
+ private _selectedColumns: string[]
private serverConfig: ServerConfig
constructor (
return this._selectedColumns
}
- set selectedColumns (val) {
+ set selectedColumns (val: string[]) {
this._selectedColumns = val
}
]
this.columns = [
- { key: 'username', label: 'Username' },
- { key: 'email', label: 'Email' },
- { key: 'quota', label: 'Video quota' },
- { key: 'role', label: 'Role' },
- { key: 'createdAt', label: 'Created' }
+ { id: 'username', label: 'Username' },
+ { id: 'email', label: 'Email' },
+ { id: 'quota', label: 'Video quota' },
+ { id: 'role', label: 'Role' },
+ { id: 'createdAt', label: 'Created' }
]
- this.selectedColumns = [ ...this.columns ] // make a full copy of the array
- this.columns.push({ key: 'quotaDaily', label: 'Daily quota' })
- this.columns.push({ key: 'pluginAuth', label: 'Auth plugin' })
- this.columns.push({ key: 'lastLoginDate', label: 'Last login' })
+
+ this.selectedColumns = this.columns.map(c => c.id)
+
+ this.columns.push({ id: 'quotaDaily', label: 'Daily quota' })
+ this.columns.push({ id: 'pluginAuth', label: 'Auth plugin' })
+ this.columns.push({ id: 'lastLoginDate', label: 'Last login' })
}
getIdentifier () {
}
}
- getColumn (key: string) {
- return this.selectedColumns.find((col: { key: string }) => col.key === key)
+ isSelected (id: string) {
+ return this.selectedColumns.find(c => c === id)
+ }
+
+ getColumn (id: string) {
+ return this.columns.find(c => c.id === id)
}
getUserVideoQuotaPercentage (user: UserForList) {
-import { FormReactive } from '@app/shared/shared-forms'
-import { SelectChannelItem } from '@app/shared/shared-forms/select-channel.component'
+import { FormReactive, SelectChannelItem } from '@app/shared/shared-forms'
import { VideoConstant, VideoPlaylistPrivacy } from '@shared/models'
import { VideoPlaylist } from '@shared/models/videos/playlist/video-playlist.model'
import { FormArray, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'
import { ServerService } from '@app/core'
import { removeElementFromArray } from '@app/helpers'
-import { FormReactiveValidationMessages, FormValidatorService, VideoValidatorsService } from '@app/shared/shared-forms'
-import { SelectChannelItem } from '@app/shared/shared-forms/select-channel.component'
+import { FormReactiveValidationMessages, FormValidatorService, SelectChannelItem, VideoValidatorsService } from '@app/shared/shared-forms'
import { InstanceService } from '@app/shared/shared-instance'
import { VideoCaptionEdit, VideoEdit, VideoService } from '@app/shared/shared-main'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { Directive, EventEmitter, OnInit } from '@angular/core'
import { AuthService, CanComponentDeactivateResult, Notifier, ServerService } from '@app/core'
import { populateAsyncUserVideoChannels } from '@app/helpers'
-import { FormReactive } from '@app/shared/shared-forms'
-import { SelectChannelItem } from '@app/shared/shared-forms/select-channel.component'
+import { FormReactive, SelectChannelItem } from '@app/shared/shared-forms'
import { VideoCaptionEdit, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
import { LoadingBarService } from '@ngx-loading-bar/core'
import { ServerConfig, VideoConstant, VideoPrivacy } from '@shared/models'
import { Component, HostListener, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import { Notifier } from '@app/core'
-import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
-import { SelectChannelItem } from '@app/shared/shared-forms/select-channel.component'
+import { FormReactive, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms'
import { VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main'
import { LoadingBarService } from '@ngx-loading-bar/core'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { DatePipe } from '@angular/common'
+import { SelectChannelItem } from '@app/shared/shared-forms'
import { environment } from '../../environments/environment'
import { AuthService } from '../core/auth'
-import { SelectChannelItem } from '@app/shared/shared-forms/select-channel.component'
// Thanks: https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
function getParameterByName (name: string, url: string) {
export * from './form-validators'
export * from './form-reactive'
+export * from './select'
export * from './input-readonly-copy.component'
export * from './markdown-textarea.component'
export * from './peertube-checkbox.component'
+++ /dev/null
-$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;
-}
--- /dev/null
+export * from './select-channel.component'
+export * from './select-options.component'
+export * from './select-tags.component'
+export * from './select-checkbox.component'
import { Component, forwardRef, Input } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
-import { Actor } from '../shared-main'
+import { Actor } from '@app/shared/shared-main/account/actor.model'
export type SelectChannelItem = {
id: number
--- /dev/null
+<ng-select
+ [items]="availableItems"
+ [(ngModel)]="selectedItems"
+ (ngModelChange)="onModelChange()"
+ i18n-placeholder placeholder="Add a new language"
+ [clearable]="true"
+ [multiple]="true"
+ [searchable]="true"
+ [closeOnSelect]="false"
+
+ bindValue="id"
+ bindLabel="label"
+
+ notFoundText="No items found" i18n-notFoundText
+
+ [selectableGroup]="selectableGroup"
+ [selectableGroupAsModel]="selectableGroupAsModel"
+
+ groupBy="group"
+ [compareWith]="compareFn"
+
+ [maxSelectedItems]="maxSelectedItems"
+>
+
+ <ng-template ng-optgroup-tmp let-item="item" let-item$="item$" let-index="index">
+ <div class="form-group-checkbox">
+ <input id="item-{{index}}" type="checkbox" [ngModel]="item$.selected"/>
+ <span role="checkbox" [attr.aria-checked]="item$.selected"></span>
+ <span>{{ item.group }}</span>
+ </div>
+ </ng-template>
+
+ <ng-template ng-option-tmp let-item="item" let-item$="item$" let-index="index">
+ <div class="form-group-checkbox">
+ <input id="item-{{index}}" type="checkbox" [ngModel]="item$.selected"/>
+ <span role="checkbox" [attr.aria-checked]="item$.selected"></span>
+ <span>{{ item.label }}</span>
+ </div>
+ </ng-template>
+
+</ng-select>
--- /dev/null
+@import '_variables';
+@import '_mixins';
+
+ng-select ::ng-deep {
+ .ng-option {
+ display: flex;
+ align-items: center;
+ }
+
+ .form-group-checkbox {
+ display: flex;
+ align-items: center;
+
+ input {
+ @include peertube-checkbox(1px);
+ }
+ }
+}
--- /dev/null
+import { Component, Input, forwardRef } from '@angular/core'
+import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'
+import { SelectOptionsItem } from './select-options.component'
+
+export type ItemSelectCheckboxValue = { id?: string | number, group?: string } | string
+
+@Component({
+ selector: 'my-select-checkbox',
+ styleUrls: [ './select-shared.component.scss', 'select-checkbox.component.scss' ],
+ templateUrl: './select-checkbox.component.html',
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => SelectCheckboxComponent),
+ multi: true
+ }
+ ]
+})
+export class SelectCheckboxComponent implements ControlValueAccessor {
+ @Input() availableItems: SelectOptionsItem[] = []
+ @Input() selectedItems: ItemSelectCheckboxValue[] = []
+ @Input() selectableGroup: boolean
+ @Input() selectableGroupAsModel: boolean
+ @Input() maxSelectedItems: number
+
+ propagateChange = (_: any) => { /* empty */ }
+
+ writeValue (items: ItemSelectCheckboxValue[]) {
+ if (Array.isArray(items)) {
+ this.selectedItems = items.map(i => {
+ if (typeof i === 'string' || typeof i === 'number') {
+ return i + ''
+ }
+
+ if (i.group) {
+ return { group: i.group }
+ }
+
+ return { id: i.id + '' }
+ })
+ } else {
+ this.selectedItems = items
+ }
+
+ this.propagateChange(this.selectedItems)
+ }
+
+ registerOnChange (fn: (_: any) => void) {
+ this.propagateChange = fn
+ }
+
+ registerOnTouched () {
+ // Unused
+ }
+
+ onModelChange () {
+ this.propagateChange(this.selectedItems)
+ }
+
+ compareFn (item: SelectOptionsItem, selected: ItemSelectCheckboxValue) {
+ if (typeof selected === 'string') {
+ return item.id === selected
+ }
+
+ if (this.selectableGroup && item.group && selected.group) {
+ return item.group === selected.group
+ }
+
+ if (selected.id && item.id) {
+ return item.id === selected.id
+ }
+
+ return false
+ }
+}
[groupBy]="groupBy"
[(ngModel)]="selectedId"
(ngModelChange)="onModelChange()"
- [bindLabel]="bindLabel"
- [bindValue]="bindValue"
[clearable]="clearable"
[searchable]="searchable"
+
+ bindLabel="label"
+ bindValue="id"
>
<ng-template ng-option-tmp let-item="item" let-index="index">
{{ item.label }}
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 }
+export type SelectOptionsItem = {
+ id: string | number
+ label: string
+ description?: string
+ group?: string
+ groupLabel?: string
+}
@Component({
selector: 'my-select-options',
@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) {
--- /dev/null
+@import '_variables';
+@import '_mixins';
+
+$form-base-input-width: auto;
+
+ng-select {
+ width: $form-base-input-width;
+
+ @media screen and (max-width: $form-base-input-width) {
+ width: 100%;
+ }
+}
+
+ng-select ::ng-deep {
+ .ng-value-container {
+ max-height: 100px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ }
+
+ // make sure the image is vertically adjusted
+ .ng-value-label img {
+ position: relative;
+ top: -1px;
+ }
+
+ img {
+ border-radius: 50%;
+ height: 20px;
+ width: 20px;
+ }
+}
}
onModelChange () {
- console.log(this.selectedItems)
-
this.propagateChange(this.selectedItems)
}
}
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { InputMaskModule } from 'primeng/inputmask'
import { InputSwitchModule } from 'primeng/inputswitch'
-import { MultiSelectModule } from 'primeng/multiselect'
import { NgSelectModule } from '@ng-select/ng-select'
import { BatchDomainsValidatorsService } from '@app/shared/shared-forms/form-validators/batch-domains-validators.service'
import { SharedGlobalIconModule } from '../shared-icons'
import { ReactiveFileComponent } from './reactive-file.component'
import { TextareaAutoResizeDirective } from './textarea-autoresize.directive'
import { TimestampInputComponent } from './timestamp-input.component'
-import { SelectChannelComponent } from './select-channel.component'
-import { SelectOptionsComponent } from './select-options.component'
-import { SelectTagsComponent } from './select-tags.component'
+import { SelectChannelComponent, SelectCheckboxComponent, SelectOptionsComponent, SelectTagsComponent } from './select'
@NgModule({
imports: [
InputMaskModule,
InputSwitchModule,
- MultiSelectModule,
NgSelectModule,
SharedMainModule,
ReactiveFileComponent,
TextareaAutoResizeDirective,
TimestampInputComponent,
+
SelectChannelComponent,
SelectOptionsComponent,
- SelectTagsComponent
+ SelectTagsComponent,
+ SelectCheckboxComponent
],
exports: [
InputMaskModule,
InputSwitchModule,
- MultiSelectModule,
NgSelectModule,
InputReadonlyCopyComponent,
ReactiveFileComponent,
TextareaAutoResizeDirective,
TimestampInputComponent,
+
SelectChannelComponent,
SelectOptionsComponent,
- SelectTagsComponent
+ SelectTagsComponent,
+ SelectCheckboxComponent
],
providers: [
</my-help>
<div>
- <p-multiSelect
- inputId="videoLanguages" [options]="languageItems" formControlName="videoLanguages" [showToggleAll]="true"
- [defaultLabel]="getDefaultVideoLanguageLabel()" [selectedItemsLabel]="getSelectedVideoLanguageLabel()"
- emptyFilterMessage="No results found" i18n-emptyFilterMessage
- ></p-multiSelect>
+ <my-select-checkbox
+ formControlName="videoLanguages" [availableItems]="languageItems"
+ [selectableGroup]="true" [selectableGroupAsModel]="true"
+ >
+ </my-select-checkbox >
</div>
</div>
margin-bottom: 30px;
}
+my-select-checkbox {
+ @include ng-select(340px);
+}
+
.form-group-select {
margin-bottom: 30px;
}
import { pick } from 'lodash-es'
-import { SelectItem } from 'primeng/api'
import { forkJoin, Subject, Subscription } from 'rxjs'
import { first } from 'rxjs/operators'
import { Component, Input, OnDestroy, OnInit } from '@angular/core'
import { AuthService, Notifier, ServerService, User, UserService } from '@app/core'
-import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
+import { FormReactive, FormValidatorService, ItemSelectCheckboxValue, SelectOptionsItem } from '@app/shared/shared-forms'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { UserUpdateMe } from '@shared/models'
import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type'
@Input() notifyOnUpdate = true
@Input() userInformationLoaded: Subject<any>
- languageItems: SelectItem[] = []
+ languageItems: SelectOptionsItem[] = []
defaultNSFWPolicy: NSFWPolicyType
formValuesWatcher: Subscription
+ private allLanguagesGroup: string
+
constructor (
protected formValidatorService: FormValidatorService,
private authService: AuthService,
}
ngOnInit () {
+ this.allLanguagesGroup = this.i18n('All languages')
+
let oldForm: any
this.buildForm({
this.serverService.getConfig(),
this.userInformationLoaded.pipe(first())
]).subscribe(([ languages, config ]) => {
- this.languageItems = [ { label: this.i18n('Unknown language'), value: '_unknown' } ]
+ const group = this.allLanguagesGroup
+
+ this.languageItems = [ { label: this.i18n('Unknown language'), id: '_unknown', group } ]
this.languageItems = this.languageItems
- .concat(languages.map(l => ({ label: l.label, value: l.id })))
+ .concat(languages.map(l => ({ label: l.label, id: l.id, group })))
- const videoLanguages = this.user.videoLanguages
- ? this.user.videoLanguages
- : this.languageItems.map(l => l.value)
+ const videoLanguages: ItemSelectCheckboxValue[] = this.user.videoLanguages
+ ? this.user.videoLanguages.map(l => ({ id: l }))
+ : [ { group } ]
this.defaultNSFWPolicy = config.instance.defaultNSFWPolicy
if (this.reactiveUpdate) {
oldForm = { ...this.form.value }
+
this.formValuesWatcher = this.form.valueChanges.subscribe((formValue: any) => {
const updatedKey = Object.keys(formValue).find(k => formValue[k] !== oldForm[k])
oldForm = { ...this.form.value }
- this.updateDetails([updatedKey])
+
+ this.updateDetails([ updatedKey ])
})
}
})
const autoPlayNextVideo = this.form.value['autoPlayNextVideo']
let videoLanguages: string[] = this.form.value['videoLanguages']
+
if (Array.isArray(videoLanguages)) {
- if (videoLanguages.length === this.languageItems.length) {
- videoLanguages = null // null means "All"
- } else if (videoLanguages.length > 20) {
- this.notifier.error('Too many languages are enabled. Please enable them all or stay below 20 enabled languages.')
+ if (videoLanguages.length > 20) {
+ this.notifier.error(this.i18n('Too many languages are enabled. Please enable them all or stay below 20 enabled languages.'))
return
- } else if (videoLanguages.length === 0) {
- this.notifier.error('You need to enabled at least 1 video language.')
+ }
+
+ if (videoLanguages.length === 0) {
+ this.notifier.error(this.i18n('You need to enable at least 1 video language.'))
return
}
+
+ if (
+ videoLanguages.length === this.languageItems.length ||
+ (videoLanguages.length === 1 && videoLanguages[0] === this.allLanguagesGroup)
+ ) {
+ videoLanguages = null // null means "All"
+ }
}
let details: UserUpdateMe = {
z-index: 100;
}
+
+@mixin ng-select ($width) {
+ ::ng-deep ng-select {
+ width: $width;
+
+ @media screen and (max-width: $width) {
+ width: 100%;
+ }
+ }
+}
+
@mixin peertube-select-container ($width) {
padding: 0;
margin: 0;
}
}
-// multiselect customizations
-p-multiselect {
- .ui-multiselect {
- border-color: #C6C6C6;
-
- &:not(.ui-state-disabled) {
- &:hover {
- border-color: #C6C6C6;
- }
-
- &:focus,
- &.ui-state-focus {
- box-shadow: #{$focus-box-shadow-form} pvar(--mainColorLightest);
- }
- }
- }
-
- .ui-multiselect-label {
- font-size: 15px !important;
- padding: 4px 30px 4px 12px !important;
-
- $width: 338px;
- width: $width !important;
-
- @media screen and (max-width: $width) {
- width: 100% !important;
- }
- }
-
- .pi.pi-chevron-down {
- margin-left: 0 !important;
-
- &::after {
- @include select-arrow-down;
-
- right: 0;
- margin-top: 6px;
- }
- }
-
- .ui-chkbox-icon {
- //position: absolute !important;
- width: 18px;
- height: 18px;
- //left: 0;
-
- //&::after {
- // left: -2px !important;
- //}
- }
-
- .ui-multiselect-panel .ui-multiselect-items .ui-multiselect-item.ui-state-highlight {
- background-color: pvar(--mainColorLighter);
- }
-
- .ui-inputtext:enabled:focus:not(.ui-state-error) {
- border-color: pvar(--mainColorLighter) !important;
- box-shadow: none;
- }
-}
-
// PrimeNG calendar tweaks
p-calendar .ui-datepicker {
a {