mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-02-25 18:55:32 -06:00
Add language filters in user preferences
This commit is contained in:
@@ -7,6 +7,9 @@
|
||||
<div i18n class="account-title">Profile</div>
|
||||
<my-account-profile [user]="user" [userInformationLoaded]="userInformationLoaded"></my-account-profile>
|
||||
|
||||
<div i18n class="account-title">Video settings</div>
|
||||
<my-account-video-settings [user]="user" [userInformationLoaded]="userInformationLoaded"></my-account-video-settings>
|
||||
|
||||
<div i18n class="account-title" id="notifications">Notifications</div>
|
||||
<my-account-notification-preferences [user]="user" [userInformationLoaded]="userInformationLoaded"></my-account-notification-preferences>
|
||||
|
||||
@@ -16,8 +19,5 @@
|
||||
<div i18n class="account-title">Email</div>
|
||||
<my-account-change-email></my-account-change-email>
|
||||
|
||||
<div i18n class="account-title">Video settings</div>
|
||||
<my-account-video-settings [user]="user" [userInformationLoaded]="userInformationLoaded"></my-account-video-settings>
|
||||
|
||||
<div i18n class="account-title">Danger zone</div>
|
||||
<my-account-danger-zone [user]="user"></my-account-danger-zone>
|
||||
|
||||
@@ -15,6 +15,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="videoLanguages">Only display videos in the following languages</label>
|
||||
<my-help i18n-customHtml
|
||||
customHtml="In Recently added, Trending, Local and Search pages"
|
||||
></my-help>
|
||||
|
||||
<div>
|
||||
<p-multiSelect
|
||||
[options]="languageItems" formControlName="videoLanguages" showToggleAll="true"
|
||||
[defaultLabel]="getDefaultVideoLanguageLabel()" [selectedItemsLabel]="getSelectedVideoLanguageLabel()"
|
||||
emptyFilterMessage="No results found" i18n-emptyFilterMessage
|
||||
></p-multiSelect>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="webTorrentEnabled" formControlName="webTorrentEnabled"
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Component, Input, OnInit } from '@angular/core'
|
||||
import { Notifier } from '@app/core'
|
||||
import { Notifier, ServerService } from '@app/core'
|
||||
import { UserUpdateMe } from '../../../../../../shared'
|
||||
import { AuthService } from '../../../core'
|
||||
import { FormReactive, User, UserService } from '../../../shared'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
|
||||
import { Subject } from 'rxjs'
|
||||
import { SelectItem } from 'primeng/api'
|
||||
import { switchMap } from 'rxjs/operators'
|
||||
|
||||
@Component({
|
||||
selector: 'my-account-video-settings',
|
||||
@@ -16,11 +18,14 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
|
||||
@Input() user: User = null
|
||||
@Input() userInformationLoaded: Subject<any>
|
||||
|
||||
languageItems: SelectItem[] = []
|
||||
|
||||
constructor (
|
||||
protected formValidatorService: FormValidatorService,
|
||||
private authService: AuthService,
|
||||
private notifier: Notifier,
|
||||
private userService: UserService,
|
||||
private serverService: ServerService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
super()
|
||||
@@ -30,31 +35,60 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
|
||||
this.buildForm({
|
||||
nsfwPolicy: null,
|
||||
webTorrentEnabled: null,
|
||||
autoPlayVideo: null
|
||||
autoPlayVideo: null,
|
||||
videoLanguages: null
|
||||
})
|
||||
|
||||
this.userInformationLoaded.subscribe(() => {
|
||||
this.form.patchValue({
|
||||
nsfwPolicy: this.user.nsfwPolicy,
|
||||
webTorrentEnabled: this.user.webTorrentEnabled,
|
||||
autoPlayVideo: this.user.autoPlayVideo === true
|
||||
})
|
||||
})
|
||||
this.serverService.videoLanguagesLoaded
|
||||
.pipe(switchMap(() => this.userInformationLoaded))
|
||||
.subscribe(() => {
|
||||
const languages = this.serverService.getVideoLanguages()
|
||||
|
||||
this.languageItems = [ { label: this.i18n('Unknown language'), value: '_unknown' } ]
|
||||
this.languageItems = this.languageItems
|
||||
.concat(languages.map(l => ({ label: l.label, value: l.id })))
|
||||
|
||||
const videoLanguages = this.user.videoLanguages
|
||||
? this.user.videoLanguages
|
||||
: this.languageItems.map(l => l.value)
|
||||
|
||||
this.form.patchValue({
|
||||
nsfwPolicy: this.user.nsfwPolicy,
|
||||
webTorrentEnabled: this.user.webTorrentEnabled,
|
||||
autoPlayVideo: this.user.autoPlayVideo === true,
|
||||
videoLanguages
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
updateDetails () {
|
||||
const nsfwPolicy = this.form.value['nsfwPolicy']
|
||||
const webTorrentEnabled = this.form.value['webTorrentEnabled']
|
||||
const autoPlayVideo = this.form.value['autoPlayVideo']
|
||||
|
||||
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.')
|
||||
return
|
||||
} else if (videoLanguages.length === 0) {
|
||||
this.notifier.error('You need to enabled at least 1 video language.')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const details: UserUpdateMe = {
|
||||
nsfwPolicy,
|
||||
webTorrentEnabled,
|
||||
autoPlayVideo
|
||||
autoPlayVideo,
|
||||
videoLanguages
|
||||
}
|
||||
|
||||
this.userService.updateMyProfile(details).subscribe(
|
||||
() => {
|
||||
this.notifier.success(this.i18n('Information updated.'))
|
||||
this.notifier.success(this.i18n('Video settings updated.'))
|
||||
|
||||
this.authService.refreshUserInformation()
|
||||
},
|
||||
@@ -62,4 +96,12 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
getDefaultVideoLanguageLabel () {
|
||||
return this.i18n('No language')
|
||||
}
|
||||
|
||||
getSelectedVideoLanguageLabel () {
|
||||
return this.i18n('{{\'{0} languages selected')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,18 +25,13 @@ import { MyAccountServerBlocklistComponent } from '@app/+my-account/my-account-b
|
||||
import { MyAccountHistoryComponent } from '@app/+my-account/my-account-history/my-account-history.component'
|
||||
import { MyAccountNotificationsComponent } from '@app/+my-account/my-account-notifications/my-account-notifications.component'
|
||||
import { MyAccountNotificationPreferencesComponent } from '@app/+my-account/my-account-settings/my-account-notification-preferences'
|
||||
import {
|
||||
MyAccountVideoPlaylistCreateComponent
|
||||
} from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component'
|
||||
import {
|
||||
MyAccountVideoPlaylistUpdateComponent
|
||||
} from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component'
|
||||
import { MyAccountVideoPlaylistCreateComponent } from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component'
|
||||
import { MyAccountVideoPlaylistUpdateComponent } from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component'
|
||||
import { MyAccountVideoPlaylistsComponent } from '@app/+my-account/my-account-video-playlists/my-account-video-playlists.component'
|
||||
import {
|
||||
MyAccountVideoPlaylistElementsComponent
|
||||
} from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component'
|
||||
import { MyAccountVideoPlaylistElementsComponent } from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component'
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop'
|
||||
import { MyAccountChangeEmailComponent } from '@app/+my-account/my-account-settings/my-account-change-email'
|
||||
import { MultiSelectModule } from 'primeng/primeng'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -46,7 +41,8 @@ import { MyAccountChangeEmailComponent } from '@app/+my-account/my-account-setti
|
||||
SharedModule,
|
||||
TableModule,
|
||||
InputSwitchModule,
|
||||
DragDropModule
|
||||
DragDropModule,
|
||||
MultiSelectModule
|
||||
],
|
||||
|
||||
declarations: [
|
||||
|
||||
@@ -18,6 +18,7 @@ export class User implements UserServerModel {
|
||||
webTorrentEnabled: boolean
|
||||
autoPlayVideo: boolean
|
||||
videosHistoryEnabled: boolean
|
||||
videoLanguages: string[]
|
||||
|
||||
videoQuota: number
|
||||
videoQuotaDaily: number
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { debounceTime } from 'rxjs/operators'
|
||||
import { debounceTime, first, tap } from 'rxjs/operators'
|
||||
import { OnDestroy, OnInit } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { fromEvent, Observable, Subscription } from 'rxjs'
|
||||
import { fromEvent, Observable, of, Subscription } from 'rxjs'
|
||||
import { AuthService } from '../../core/auth'
|
||||
import { ComponentPagination } from '../rest/component-pagination.model'
|
||||
import { VideoSortField } from './sort-field.type'
|
||||
@@ -32,18 +32,20 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
|
||||
sort: VideoSortField = '-publishedAt'
|
||||
|
||||
categoryOneOf?: number
|
||||
languageOneOf?: string[]
|
||||
defaultSort: VideoSortField = '-publishedAt'
|
||||
|
||||
syndicationItems: Syndication[] = []
|
||||
|
||||
loadOnInit = true
|
||||
videos: Video[] = []
|
||||
useUserVideoLanguagePreferences = false
|
||||
ownerDisplayType: OwnerDisplayType = 'account'
|
||||
displayModerationBlock = false
|
||||
titleTooltip: string
|
||||
displayVideoActions = true
|
||||
groupByDate = false
|
||||
|
||||
videos: Video[] = []
|
||||
disabled = false
|
||||
|
||||
displayOptions: MiniatureDisplayOptions = {
|
||||
@@ -98,7 +100,12 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
|
||||
.subscribe(() => this.calcPageSizes())
|
||||
|
||||
this.calcPageSizes()
|
||||
if (this.loadOnInit === true) this.loadMoreVideos()
|
||||
|
||||
const loadUserObservable = this.loadUserVideoLanguagesIfNeeded()
|
||||
|
||||
if (this.loadOnInit === true) {
|
||||
loadUserObservable.subscribe(() => this.loadMoreVideos())
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
@@ -245,4 +252,16 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
|
||||
|
||||
this.router.navigate([ path ], { queryParams, replaceUrl: true, queryParamsHandling: 'merge' })
|
||||
}
|
||||
|
||||
private loadUserVideoLanguagesIfNeeded () {
|
||||
if (!this.authService.isLoggedIn() || !this.useUserVideoLanguagePreferences) {
|
||||
return of(true)
|
||||
}
|
||||
|
||||
return this.authService.userInformationLoaded
|
||||
.pipe(
|
||||
first(),
|
||||
tap(() => this.languageOneOf = this.user.videoLanguages)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,12 +35,13 @@ import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
|
||||
import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
|
||||
|
||||
export interface VideosProvider {
|
||||
getVideos (
|
||||
getVideos (parameters: {
|
||||
videoPagination: ComponentPagination,
|
||||
sort: VideoSortField,
|
||||
filter?: VideoFilter,
|
||||
categoryOneOf?: number
|
||||
): Observable<{ videos: Video[], totalVideos: number }>
|
||||
categoryOneOf?: number,
|
||||
languageOneOf?: string[]
|
||||
}): Observable<{ videos: Video[], totalVideos: number }>
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@@ -206,12 +207,15 @@ export class VideoService implements VideosProvider {
|
||||
)
|
||||
}
|
||||
|
||||
getVideos (
|
||||
getVideos (parameters: {
|
||||
videoPagination: ComponentPagination,
|
||||
sort: VideoSortField,
|
||||
filter?: VideoFilter,
|
||||
categoryOneOf?: number
|
||||
): Observable<{ videos: Video[], totalVideos: number }> {
|
||||
categoryOneOf?: number,
|
||||
languageOneOf?: string[]
|
||||
}): Observable<{ videos: Video[], totalVideos: number }> {
|
||||
const { videoPagination, sort, filter, categoryOneOf, languageOneOf } = parameters
|
||||
|
||||
const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
|
||||
|
||||
let params = new HttpParams()
|
||||
@@ -225,6 +229,12 @@ export class VideoService implements VideosProvider {
|
||||
params = params.set('categoryOneOf', categoryOneOf + '')
|
||||
}
|
||||
|
||||
if (languageOneOf) {
|
||||
for (const l of languageOneOf) {
|
||||
params = params.append('languageOneOf[]', l)
|
||||
}
|
||||
}
|
||||
|
||||
return this.authHttp
|
||||
.get<ResultList<Video>>(VideoService.BASE_VIDEO_URL, { params })
|
||||
.pipe(
|
||||
|
||||
@@ -32,7 +32,7 @@ export class RecentVideosRecommendationService implements RecommendationService
|
||||
|
||||
private fetchPage (page: number, recommendation: RecommendationInfo): Observable<Video[]> {
|
||||
const pagination = { currentPage: page, itemsPerPage: this.pageSize + 1 }
|
||||
const defaultSubscription = this.videos.getVideos(pagination, '-createdAt')
|
||||
const defaultSubscription = this.videos.getVideos({ videoPagination: pagination, sort: '-createdAt' })
|
||||
.pipe(map(v => v.videos))
|
||||
|
||||
if (!recommendation.tags || recommendation.tags.length === 0) return defaultSubscription
|
||||
|
||||
@@ -21,6 +21,8 @@ export class VideoLocalComponent extends AbstractVideoList implements OnInit, On
|
||||
sort = '-publishedAt' as VideoSortField
|
||||
filter: VideoFilter = 'local'
|
||||
|
||||
useUserVideoLanguagePreferences = true
|
||||
|
||||
constructor (
|
||||
protected i18n: I18n,
|
||||
protected router: Router,
|
||||
@@ -54,7 +56,13 @@ export class VideoLocalComponent extends AbstractVideoList implements OnInit, On
|
||||
getVideosObservable (page: number) {
|
||||
const newPagination = immutableAssign(this.pagination, { currentPage: page })
|
||||
|
||||
return this.videoService.getVideos(newPagination, this.sort, this.filter, this.categoryOneOf)
|
||||
return this.videoService.getVideos({
|
||||
videoPagination: newPagination,
|
||||
sort: this.sort,
|
||||
filter: this.filter,
|
||||
categoryOneOf: this.categoryOneOf,
|
||||
languageOneOf: this.languageOneOf
|
||||
})
|
||||
}
|
||||
|
||||
generateSyndicationList () {
|
||||
|
||||
@@ -19,6 +19,8 @@ export class VideoRecentlyAddedComponent extends AbstractVideoList implements On
|
||||
sort: VideoSortField = '-publishedAt'
|
||||
groupByDate = true
|
||||
|
||||
useUserVideoLanguagePreferences = true
|
||||
|
||||
constructor (
|
||||
protected i18n: I18n,
|
||||
protected route: ActivatedRoute,
|
||||
@@ -47,7 +49,13 @@ export class VideoRecentlyAddedComponent extends AbstractVideoList implements On
|
||||
getVideosObservable (page: number) {
|
||||
const newPagination = immutableAssign(this.pagination, { currentPage: page })
|
||||
|
||||
return this.videoService.getVideos(newPagination, this.sort, undefined, this.categoryOneOf)
|
||||
return this.videoService.getVideos({
|
||||
videoPagination: newPagination,
|
||||
sort: this.sort,
|
||||
filter: undefined,
|
||||
categoryOneOf: this.categoryOneOf,
|
||||
languageOneOf: this.languageOneOf
|
||||
})
|
||||
}
|
||||
|
||||
generateSyndicationList () {
|
||||
|
||||
@@ -18,6 +18,8 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
|
||||
titlePage: string
|
||||
defaultSort: VideoSortField = '-trending'
|
||||
|
||||
useUserVideoLanguagePreferences = true
|
||||
|
||||
constructor (
|
||||
protected i18n: I18n,
|
||||
protected router: Router,
|
||||
@@ -59,7 +61,13 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
|
||||
|
||||
getVideosObservable (page: number) {
|
||||
const newPagination = immutableAssign(this.pagination, { currentPage: page })
|
||||
return this.videoService.getVideos(newPagination, this.sort, undefined, this.categoryOneOf)
|
||||
return this.videoService.getVideos({
|
||||
videoPagination: newPagination,
|
||||
sort: this.sort,
|
||||
filter: undefined,
|
||||
categoryOneOf: this.categoryOneOf,
|
||||
languageOneOf: this.languageOneOf
|
||||
})
|
||||
}
|
||||
|
||||
generateSyndicationList () {
|
||||
|
||||
@@ -224,6 +224,20 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@mixin select-arrow-down {
|
||||
top: 50%;
|
||||
right: calc(0% + 15px);
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border: 5px solid rgba(0, 0, 0, 0);
|
||||
border-top-color: #000;
|
||||
margin-top: -2px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
@mixin peertube-select-container ($width) {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@@ -248,17 +262,7 @@
|
||||
}
|
||||
|
||||
&:after {
|
||||
top: 50%;
|
||||
right: calc(0% + 15px);
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border: 5px solid rgba(0, 0, 0, 0);
|
||||
border-top-color: #000;
|
||||
margin-top: -2px;
|
||||
z-index: 100;
|
||||
@include select-arrow-down;
|
||||
}
|
||||
|
||||
select {
|
||||
|
||||
@@ -232,6 +232,43 @@ p-table {
|
||||
}
|
||||
}
|
||||
|
||||
// multiselect customizations
|
||||
p-multiselect {
|
||||
.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;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
// PrimeNG calendar tweaks
|
||||
p-calendar .ui-datepicker {
|
||||
a {
|
||||
|
||||
Reference in New Issue
Block a user