aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app')
-rw-r--r--client/src/app/+about/about-follows/about-follows.component.ts2
-rw-r--r--client/src/app/+about/about-instance/about-instance.component.ts2
-rw-r--r--client/src/app/+about/about-instance/contact-admin-modal.component.ts8
-rw-r--r--client/src/app/+about/about-routing.module.ts1
-rw-r--r--client/src/app/+accounts/account-video-channels/account-video-channels.component.ts12
-rw-r--r--client/src/app/+accounts/account-videos/account-videos.component.ts2
-rw-r--r--client/src/app/+accounts/accounts.component.html6
-rw-r--r--client/src/app/+accounts/accounts.component.ts14
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts2
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts4
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-homepage.component.ts2
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.ts3
-rw-r--r--client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts2
-rw-r--r--client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts4
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.html4
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts4
-rw-r--r--client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts6
-rw-r--r--client/src/app/+admin/plugins/plugin-search/plugin-search.component.html2
-rw-r--r--client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts6
-rw-r--r--client/src/app/+admin/plugins/shared/plugin-api.service.ts4
-rw-r--r--client/src/app/+admin/system/jobs/job.service.ts8
-rw-r--r--client/src/app/+admin/system/logs/logs.service.ts6
-rw-r--r--client/src/app/+admin/users/user-edit/user-create.component.ts6
-rw-r--r--client/src/app/+admin/users/user-edit/user-edit.ts2
-rw-r--r--client/src/app/+admin/users/user-edit/user-password.component.ts6
-rw-r--r--client/src/app/+admin/users/user-edit/user-update.component.ts14
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.ts2
-rw-r--r--client/src/app/+login/login.component.html2
-rw-r--r--client/src/app/+login/login.component.ts2
-rw-r--r--client/src/app/+my-account/my-account-applications/my-account-applications.component.ts1
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts6
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts14
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts3
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-settings.component.ts2
-rw-r--r--client/src/app/+my-account/my-account.component.ts2
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts4
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts8
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts8
-rw-r--r--client/src/app/+my-library/my-history/my-history.component.ts7
-rw-r--r--client/src/app/+my-library/my-library.component.ts2
-rw-r--r--client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.ts2
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts6
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.ts4
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlist-update.component.ts10
-rw-r--r--client/src/app/+my-library/my-videos/modals/video-change-ownership.component.ts6
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.ts4
-rw-r--r--client/src/app/+remote-interaction/remote-interaction.component.ts2
-rw-r--r--client/src/app/+reset-password/reset-password.component.ts2
-rw-r--r--client/src/app/+signup/+register/custom-stepper.component.ts6
-rw-r--r--client/src/app/+signup/+register/register.component.ts11
-rw-r--r--client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts2
-rw-r--r--client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts2
-rw-r--r--client/src/app/+video-channels/video-channels.component.html2
-rw-r--r--client/src/app/+video-channels/video-channels.component.ts8
-rw-r--r--client/src/app/+videos/+video-edit/shared/i18n-primeng-calendar.service.ts2
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.ts8
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit-utils.ts2
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.ts10
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/drag-drop.directive.ts8
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts2
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.html2
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts4
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts2
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-send.ts2
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-upload.component.html2
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts5
-rw-r--r--client/src/app/+videos/+video-edit/video-update.component.ts5
-rw-r--r--client/src/app/+videos/+video-edit/video-update.resolver.ts6
-rw-r--r--client/src/app/+videos/+video-watch/player-styles.component.ts2
-rw-r--r--client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts6
-rw-r--r--client/src/app/+videos/+video-watch/shared/comment/video-comment.component.html2
-rw-r--r--client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts2
-rw-r--r--client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts8
-rw-r--r--client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts2
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html2
-rw-r--r--client/src/app/+videos/+video-watch/shared/playlist/video-watch-playlist.component.ts10
-rw-r--r--client/src/app/+videos/+video-watch/shared/recommendations/recommended-videos.component.html2
-rw-r--r--client/src/app/+videos/+video-watch/shared/recommendations/recommended-videos.component.ts2
-rw-r--r--client/src/app/+videos/+video-watch/shared/timestamp-route-transformer.directive.ts8
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts39
-rw-r--r--client/src/app/+videos/video-list/overview/overview.service.ts2
-rw-r--r--client/src/app/+videos/video-list/trending/video-trending-header.component.ts2
-rw-r--r--client/src/app/+videos/video-list/trending/video-trending.component.ts6
-rw-r--r--client/src/app/+videos/video-list/video-local.component.ts2
-rw-r--r--client/src/app/app.component.ts4
-rw-r--r--client/src/app/core/auth/auth.service.ts6
-rw-r--r--client/src/app/core/hotkeys/hotkeys.component.ts6
-rw-r--r--client/src/app/core/menu/menu.service.ts4
-rw-r--r--client/src/app/core/plugins/hooks.service.ts10
-rw-r--r--client/src/app/core/plugins/plugin.service.ts10
-rw-r--r--client/src/app/core/renderer/markdown.service.ts14
-rw-r--r--client/src/app/core/rest/rest-extractor.service.ts26
-rw-r--r--client/src/app/core/routing/custom-reuse-strategy.ts4
-rw-r--r--client/src/app/core/routing/menu-guard.service.ts18
-rw-r--r--client/src/app/core/routing/meta-guard.service.ts2
-rw-r--r--client/src/app/core/routing/preload-selected-modules-list.ts2
-rw-r--r--client/src/app/core/scoped-tokens/scoped-tokens.service.ts7
-rw-r--r--client/src/app/core/server/server.service.ts4
-rw-r--r--client/src/app/core/theme/theme.service.ts4
-rw-r--r--client/src/app/core/users/user.service.ts4
-rw-r--r--client/src/app/header/search-typeahead.component.ts2
-rw-r--r--client/src/app/helpers/locales/oc.ts114
-rw-r--r--client/src/app/helpers/utils.ts10
-rw-r--r--client/src/app/menu/language-chooser.component.ts2
-rw-r--r--client/src/app/menu/menu.component.ts2
-rw-r--r--client/src/app/modal/custom-modal.component.ts8
-rw-r--r--client/src/app/shared/form-validators/abuse-validators.ts24
-rw-r--r--client/src/app/shared/form-validators/custom-config-validators.ts88
-rw-r--r--client/src/app/shared/form-validators/form-validator.model.ts2
-rw-r--r--client/src/app/shared/form-validators/host-validators.ts20
-rw-r--r--client/src/app/shared/form-validators/instance-validators.ts24
-rw-r--r--client/src/app/shared/form-validators/login-validators.ts4
-rw-r--r--client/src/app/shared/form-validators/reset-password-validators.ts2
-rw-r--r--client/src/app/shared/form-validators/user-validators.ts64
-rw-r--r--client/src/app/shared/form-validators/video-block-validators.ts4
-rw-r--r--client/src/app/shared/form-validators/video-captions-validators.ts4
-rw-r--r--client/src/app/shared/form-validators/video-channel-validators.ts22
-rw-r--r--client/src/app/shared/form-validators/video-comment-validators.ts6
-rw-r--r--client/src/app/shared/form-validators/video-ownership-change-validators.ts8
-rw-r--r--client/src/app/shared/form-validators/video-playlist-validators.ts16
-rw-r--r--client/src/app/shared/form-validators/video-validators.ts32
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts10
-rw-r--r--client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts2
-rw-r--r--client/src/app/shared/shared-abuse-list/processed-abuse.model.ts2
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts2
-rw-r--r--client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts4
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar.component.ts36
-rw-r--r--client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts8
-rw-r--r--client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts4
-rw-r--r--client/src/app/shared/shared-forms/form-reactive.ts6
-rw-r--r--client/src/app/shared/shared-forms/form-validator.service.ts8
-rw-r--r--client/src/app/shared/shared-forms/reactive-file.component.ts2
-rw-r--r--client/src/app/shared/shared-icons/global-icon.component.ts106
-rw-r--r--client/src/app/shared/shared-instance/instance-about-accordion.component.ts2
-rw-r--r--client/src/app/shared/shared-instance/instance-follow.service.ts16
-rw-r--r--client/src/app/shared/shared-instance/instance-statistics.component.ts4
-rw-r--r--client/src/app/shared/shared-instance/instance.service.ts2
-rw-r--r--client/src/app/shared/shared-instance/shared-instance.module.ts1
-rw-r--r--client/src/app/shared/shared-main/account/account.model.ts2
-rw-r--r--client/src/app/shared/shared-main/account/actor.model.ts2
-rw-r--r--client/src/app/shared/shared-main/angular/autofocus.directive.ts2
-rw-r--r--client/src/app/shared/shared-main/angular/link.component.ts2
-rw-r--r--client/src/app/shared/shared-main/angular/peertube-template.directive.ts1
-rw-r--r--client/src/app/shared/shared-main/feeds/syndication.model.ts4
-rw-r--r--client/src/app/shared/shared-main/misc/help.component.ts12
-rw-r--r--client/src/app/shared/shared-main/misc/list-overflow.component.ts4
-rw-r--r--client/src/app/shared/shared-main/misc/simple-search-input.component.ts2
-rw-r--r--client/src/app/shared/shared-main/misc/top-menu-dropdown.component.ts2
-rw-r--r--client/src/app/shared/shared-main/users/user-notification.service.ts6
-rw-r--r--client/src/app/shared/shared-main/users/user-quota.component.ts2
-rw-r--r--client/src/app/shared/shared-main/video-channel/video-channel.model.ts5
-rw-r--r--client/src/app/shared/shared-main/video-channel/video-channel.service.ts12
-rw-r--r--client/src/app/shared/shared-main/video/redundancy.service.ts4
-rw-r--r--client/src/app/shared/shared-main/video/video-edit.model.ts12
-rw-r--r--client/src/app/shared/shared-main/video/video.model.ts2
-rw-r--r--client/src/app/shared/shared-main/video/video.service.ts18
-rw-r--r--client/src/app/shared/shared-moderation/abuse.service.ts13
-rw-r--r--client/src/app/shared/shared-moderation/account-blocklist.component.ts7
-rw-r--r--client/src/app/shared/shared-moderation/blocklist.service.ts8
-rw-r--r--client/src/app/shared/shared-moderation/server-blocklist.component.ts10
-rw-r--r--client/src/app/shared/shared-moderation/video-block.component.ts4
-rw-r--r--client/src/app/shared/shared-support-modal/support-modal.component.ts4
-rw-r--r--client/src/app/shared/shared-user-settings/user-video-settings.component.ts2
-rw-r--r--client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts16
-rw-r--r--client/src/app/shared/shared-user-subscription/subscribe-button.component.ts4
-rw-r--r--client/src/app/shared/shared-user-subscription/user-subscription.service.ts20
-rw-r--r--client/src/app/shared/shared-video-comment/video-comment.model.ts8
-rw-r--r--client/src/app/shared/shared-video-comment/video-comment.service.ts14
-rw-r--r--client/src/app/shared/shared-video-miniature/abstract-video-list.ts16
-rw-r--r--client/src/app/shared/shared-video-miniature/video-download.component.ts26
-rw-r--r--client/src/app/shared/shared-video-miniature/video-list-header.component.ts2
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-selection.component.ts2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist-element.model.ts2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist.model.ts2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist.service.ts10
176 files changed, 777 insertions, 706 deletions
diff --git a/client/src/app/+about/about-follows/about-follows.component.ts b/client/src/app/+about/about-follows/about-follows.component.ts
index 1dcb6396c..a35272681 100644
--- a/client/src/app/+about/about-follows/about-follows.component.ts
+++ b/client/src/app/+about/about-follows/about-follows.component.ts
@@ -90,7 +90,7 @@ export class AboutFollowsComponent implements OnInit {
90 private loadMoreFollowers (reset = false) { 90 private loadMoreFollowers (reset = false) {
91 const pagination = this.restService.componentPaginationToRestPagination(this.followersPagination) 91 const pagination = this.restService.componentPaginationToRestPagination(this.followersPagination)
92 92
93 this.followService.getFollowers({ pagination: pagination, sort: this.sort, state: 'accepted' }) 93 this.followService.getFollowers({ pagination, sort: this.sort, state: 'accepted' })
94 .subscribe({ 94 .subscribe({
95 next: resultList => { 95 next: resultList => {
96 if (reset) this.followers = [] 96 if (reset) this.followers = []
diff --git a/client/src/app/+about/about-instance/about-instance.component.ts b/client/src/app/+about/about-instance/about-instance.component.ts
index f86df5b67..0826bbc5a 100644
--- a/client/src/app/+about/about-instance/about-instance.component.ts
+++ b/client/src/app/+about/about-instance/about-instance.component.ts
@@ -95,6 +95,6 @@ export class AboutInstanceComponent implements OnInit, AfterViewChecked {
95 onClickCopyLink (anchor: HTMLAnchorElement) { 95 onClickCopyLink (anchor: HTMLAnchorElement) {
96 const link = anchor.href 96 const link = anchor.href
97 copyToClipboard(link) 97 copyToClipboard(link)
98 this.notifier.success(link, $localize `Link copied`) 98 this.notifier.success(link, $localize`Link copied`)
99 } 99 }
100} 100}
diff --git a/client/src/app/+about/about-instance/contact-admin-modal.component.ts b/client/src/app/+about/about-instance/contact-admin-modal.component.ts
index cbc759881..fab9cfc4b 100644
--- a/client/src/app/+about/about-instance/contact-admin-modal.component.ts
+++ b/client/src/app/+about/about-instance/contact-admin-modal.component.ts
@@ -77,10 +77,10 @@ export class ContactAdminModalComponent extends FormReactive implements OnInit {
77 } 77 }
78 78
79 sendForm () { 79 sendForm () {
80 const fromName = this.form.value[ 'fromName' ] 80 const fromName = this.form.value['fromName']
81 const fromEmail = this.form.value[ 'fromEmail' ] 81 const fromEmail = this.form.value['fromEmail']
82 const subject = this.form.value[ 'subject' ] 82 const subject = this.form.value['subject']
83 const body = this.form.value[ 'body' ] 83 const body = this.form.value['body']
84 84
85 this.instanceService.contactAdministrator(fromEmail, fromName, subject, body) 85 this.instanceService.contactAdministrator(fromEmail, fromName, subject, body)
86 .subscribe({ 86 .subscribe({
diff --git a/client/src/app/+about/about-routing.module.ts b/client/src/app/+about/about-routing.module.ts
index 3974231e3..cf5570d31 100644
--- a/client/src/app/+about/about-routing.module.ts
+++ b/client/src/app/+about/about-routing.module.ts
@@ -3,7 +3,6 @@ import { RouterModule, Routes } from '@angular/router'
3import { AboutFollowsComponent } from '@app/+about/about-follows/about-follows.component' 3import { AboutFollowsComponent } from '@app/+about/about-follows/about-follows.component'
4import { AboutInstanceComponent } from '@app/+about/about-instance/about-instance.component' 4import { AboutInstanceComponent } from '@app/+about/about-instance/about-instance.component'
5import { AboutInstanceResolver } from '@app/+about/about-instance/about-instance.resolver' 5import { AboutInstanceResolver } from '@app/+about/about-instance/about-instance.resolver'
6import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-admin-modal.component'
7import { AboutPeertubeComponent } from '@app/+about/about-peertube/about-peertube.component' 6import { AboutPeertubeComponent } from '@app/+about/about-peertube/about-peertube.component'
8import { AboutComponent } from './about.component' 7import { AboutComponent } from './about.component'
9 8
diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts b/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts
index e146a5cd2..f6df38857 100644
--- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts
+++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts
@@ -1,10 +1,10 @@
1import { from, Subject, Subscription } from 'rxjs' 1import { from, Subject, Subscription } from 'rxjs'
2import { concatMap, map, switchMap, tap } from 'rxjs/operators' 2import { concatMap, map, switchMap, tap } from 'rxjs/operators'
3import { Component, OnDestroy, OnInit } from '@angular/core' 3import { Component, OnDestroy, OnInit } from '@angular/core'
4import { ComponentPagination, hasMoreItems, MarkdownService, ScreenService, User, UserService } from '@app/core' 4import { ComponentPagination, hasMoreItems, MarkdownService, User, UserService } from '@app/core'
5import { Account, AccountService, Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' 5import { Account, AccountService, Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
6import { NSFWPolicyType, VideoSortField } from '@shared/models'
7import { MiniatureDisplayOptions } from '@app/shared/shared-video-miniature' 6import { MiniatureDisplayOptions } from '@app/shared/shared-video-miniature'
7import { NSFWPolicyType, VideoSortField } from '@shared/models'
8 8
9@Component({ 9@Component({
10 selector: 'my-account-video-channels', 10 selector: 'my-account-video-channels',
@@ -87,7 +87,9 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
87 87
88 this.videoChannelService.listAccountVideoChannels(options) 88 this.videoChannelService.listAccountVideoChannels(options)
89 .pipe( 89 .pipe(
90 tap(res => this.channelPagination.totalItems = res.total), 90 tap(res => {
91 this.channelPagination.totalItems = res.total
92 }),
91 switchMap(res => from(res.data)), 93 switchMap(res => from(res.data)),
92 concatMap(videoChannel => { 94 concatMap(videoChannel => {
93 const options = { 95 const options = {
@@ -113,14 +115,14 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
113 } 115 }
114 116
115 getVideosOf (videoChannel: VideoChannel) { 117 getVideosOf (videoChannel: VideoChannel) {
116 const obj = this.videos[ videoChannel.id ] 118 const obj = this.videos[videoChannel.id]
117 if (!obj) return [] 119 if (!obj) return []
118 120
119 return obj.videos 121 return obj.videos
120 } 122 }
121 123
122 getTotalVideosOf (videoChannel: VideoChannel) { 124 getTotalVideosOf (videoChannel: VideoChannel) {
123 const obj = this.videos[ videoChannel.id ] 125 const obj = this.videos[videoChannel.id]
124 if (!obj) return undefined 126 if (!obj) return undefined
125 127
126 return obj.total 128 return obj.total
diff --git a/client/src/app/+accounts/account-videos/account-videos.component.ts b/client/src/app/+accounts/account-videos/account-videos.component.ts
index 75af45e90..4ab6d2147 100644
--- a/client/src/app/+accounts/account-videos/account-videos.component.ts
+++ b/client/src/app/+accounts/account-videos/account-videos.component.ts
@@ -1,5 +1,5 @@
1import { forkJoin, Subscription } from 'rxjs' 1import { forkJoin, Subscription } from 'rxjs'
2import { first, tap } from 'rxjs/operators' 2import { first } from 'rxjs/operators'
3import { Component, ComponentFactoryResolver, OnDestroy, OnInit } from '@angular/core' 3import { Component, ComponentFactoryResolver, OnDestroy, OnInit } from '@angular/core'
4import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
5import { AuthService, ConfirmService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core' 5import { AuthService, ConfirmService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core'
diff --git a/client/src/app/+accounts/accounts.component.html b/client/src/app/+accounts/accounts.component.html
index cb3e79a18..0906992fe 100644
--- a/client/src/app/+accounts/accounts.component.html
+++ b/client/src/app/+accounts/accounts.component.html
@@ -71,14 +71,14 @@
71 <a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a> 71 <a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a>
72 </ng-template> 72 </ng-template>
73 73
74 <list-overflow [hidden]="hideMenu" [items]="links" [itemTemplate]="linkTemplate"></list-overflow> 74 <my-list-overflow [hidden]="hideMenu" [items]="links" [itemTemplate]="linkTemplate"></my-list-overflow>
75 75
76 <simple-search-input 76 <my-simple-search-input
77 [alwaysShow]="!isInSmallView()" (searchChanged)="searchChanged($event)" 77 [alwaysShow]="!isInSmallView()" (searchChanged)="searchChanged($event)"
78 (inputDisplayChanged)="onSearchInputDisplayChanged($event)" name="search-videos" 78 (inputDisplayChanged)="onSearchInputDisplayChanged($event)" name="search-videos"
79 i18n-iconTitle icon-title="Search account videos" 79 i18n-iconTitle icon-title="Search account videos"
80 i18n-placeholder placeholder="Search account videos" 80 i18n-placeholder placeholder="Search account videos"
81 ></simple-search-input> 81 ></my-simple-search-input>
82 </div> 82 </div>
83 83
84 <router-outlet (activate)="onOutletLoaded($event)"></router-outlet> 84 <router-outlet (activate)="onOutletLoaded($event)"></router-outlet>
diff --git a/client/src/app/+accounts/accounts.component.ts b/client/src/app/+accounts/accounts.component.ts
index 25eb13588..733cff8d5 100644
--- a/client/src/app/+accounts/accounts.component.ts
+++ b/client/src/app/+accounts/accounts.component.ts
@@ -61,7 +61,7 @@ export class AccountsComponent implements OnInit, OnDestroy {
61 ngOnInit () { 61 ngOnInit () {
62 this.routeSub = this.route.params 62 this.routeSub = this.route.params
63 .pipe( 63 .pipe(
64 map(params => params[ 'accountId' ]), 64 map(params => params['accountId']),
65 distinctUntilChanged(), 65 distinctUntilChanged(),
66 switchMap(accountId => this.accountService.getAccount(accountId)), 66 switchMap(accountId => this.accountService.getAccount(accountId)),
67 tap(account => this.onAccount(account)), 67 tap(account => this.onAccount(account)),
@@ -72,7 +72,9 @@ export class AccountsComponent implements OnInit, OnDestroy {
72 ])) 72 ]))
73 ) 73 )
74 .subscribe({ 74 .subscribe({
75 next: videoChannels => this.videoChannels = videoChannels.data, 75 next: videoChannels => {
76 this.videoChannels = videoChannels.data
77 },
76 78
77 error: err => this.notifier.error(err.message) 79 error: err => this.notifier.error(err.message)
78 }) 80 })
@@ -176,7 +178,9 @@ export class AccountsComponent implements OnInit, OnDestroy {
176 if (user.hasRight(UserRight.MANAGE_USERS)) { 178 if (user.hasRight(UserRight.MANAGE_USERS)) {
177 this.userService.getUser(account.userId) 179 this.userService.getUser(account.userId)
178 .subscribe({ 180 .subscribe({
179 next: accountUser => this.accountUser = accountUser, 181 next: accountUser => {
182 this.accountUser = accountUser
183 },
180 184
181 error: err => this.notifier.error(err.message) 185 error: err => this.notifier.error(err.message)
182 }) 186 })
@@ -209,6 +213,8 @@ export class AccountsComponent implements OnInit, OnDestroy {
209 itemsPerPage: 0 213 itemsPerPage: 0
210 }, 214 },
211 sort: '-publishedAt' 215 sort: '-publishedAt'
212 }).subscribe(res => this.accountVideosCount = res.total) 216 }).subscribe(res => {
217 this.accountVideosCount = res.total
218 })
213 } 219 }
214} 220}
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts
index 671e734ac..7a8258820 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts
@@ -97,7 +97,7 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
97 .pipe(pairwise()) 97 .pipe(pairwise())
98 .subscribe(([ oldValue, newValue ]) => { 98 .subscribe(([ oldValue, newValue ]) => {
99 if (oldValue !== true && newValue === true) { 99 if (oldValue !== true && newValue === true) {
100 // tslint:disable:max-line-length 100 /* eslint-disable max-len */
101 this.signupAlertMessage = $localize`You enabled signup: we automatically enabled the "Block new videos automatically" checkbox of the "Videos" section just below.` 101 this.signupAlertMessage = $localize`You enabled signup: we automatically enabled the "Block new videos automatically" checkbox of the "Videos" section just below.`
102 102
103 this.form.patchValue({ 103 this.form.patchValue({
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
index 538fa6f14..be1a99289 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
@@ -277,7 +277,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
277 277
278 // Reload general configuration 278 // Reload general configuration
279 this.serverService.resetConfig() 279 this.serverService.resetConfig()
280 .subscribe(config => this.serverConfig = config) 280 .subscribe(config => {
281 this.serverConfig = config
282 })
281 283
282 this.updateForm() 284 this.updateForm()
283 285
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-homepage.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-homepage.component.ts
index 1923ede39..dc834da14 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-homepage.component.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-homepage.component.ts
@@ -1,4 +1,4 @@
1import { Component, Input, OnInit } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { FormGroup } from '@angular/forms' 2import { FormGroup } from '@angular/forms'
3import { CustomMarkupService } from '@app/shared/shared-custom-markup' 3import { CustomMarkupService } from '@app/shared/shared-custom-markup'
4 4
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts
index cf0225098..383f66ffd 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.ts
+++ b/client/src/app/+admin/follows/following-list/following-list.component.ts
@@ -2,7 +2,6 @@ import { SortMeta } from 'primeng/api'
2import { Component, OnInit, ViewChild } from '@angular/core' 2import { Component, OnInit, ViewChild } from '@angular/core'
3import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core' 3import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
4import { InstanceFollowService } from '@app/shared/shared-instance' 4import { InstanceFollowService } from '@app/shared/shared-instance'
5import { BatchDomainsModalComponent } from '@app/shared/shared-moderation'
6import { ActorFollow } from '@shared/models' 5import { ActorFollow } from '@shared/models'
7import { FollowModalComponent } from './follow-modal.component' 6import { FollowModalComponent } from './follow-modal.component'
8 7
@@ -22,7 +21,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
22 private notifier: Notifier, 21 private notifier: Notifier,
23 private confirmService: ConfirmService, 22 private confirmService: ConfirmService,
24 private followService: InstanceFollowService 23 private followService: InstanceFollowService
25 ) { 24 ) {
26 super() 25 super()
27 } 26 }
28 27
diff --git a/client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts b/client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts
index 47c402510..95f8473db 100644
--- a/client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts
+++ b/client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts
@@ -14,7 +14,7 @@ export class RedundancyCheckboxComponent {
14 constructor ( 14 constructor (
15 private notifier: Notifier, 15 private notifier: Notifier,
16 private redundancyService: RedundancyService 16 private redundancyService: RedundancyService
17 ) { } 17 ) { }
18 18
19 updateRedundancyState () { 19 updateRedundancyState () {
20 this.redundancyService.updateRedundancy(this.host, this.redundancyAllowed) 20 this.redundancyService.updateRedundancy(this.host, this.redundancyAllowed)
diff --git a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
index 4c691269a..7ffed83e8 100644
--- a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
+++ b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
@@ -21,7 +21,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
21 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 21 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
22 displayType: VideoRedundanciesTarget = 'my-videos' 22 displayType: VideoRedundanciesTarget = 'my-videos'
23 23
24 redundanciesGraphsData: { stats: VideosRedundancyStats, graphData: object, options: object }[] = [] 24 redundanciesGraphsData: { stats: VideosRedundancyStats, graphData: any, options: any }[] = []
25 25
26 noRedundancies = false 26 noRedundancies = false
27 27
@@ -32,7 +32,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
32 private confirmService: ConfirmService, 32 private confirmService: ConfirmService,
33 private redundancyService: RedundancyService, 33 private redundancyService: RedundancyService,
34 private serverService: ServerService 34 private serverService: ServerService
35 ) { 35 ) {
36 super() 36 super()
37 37
38 this.bytesPipe = new BytesPipe() 38 this.bytesPipe = new BytesPipe()
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html
index d89c8f244..3a8df1f07 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.html
@@ -21,7 +21,7 @@
21 21
22 <ng-template pTemplate="header"> 22 <ng-template pTemplate="header">
23 <tr> 23 <tr>
24 <th style="width: 40px"></th> 24 <th style="width: 40px;"></th>
25 <th style="width: 150px;"></th> 25 <th style="width: 150px;"></th>
26 <th i18n pSortableColumn="name">Video <p-sortIcon field="name"></p-sortIcon></th> 26 <th i18n pSortableColumn="name">Video <p-sortIcon field="name"></p-sortIcon></th>
27 <th style="width: 100px;" i18n>Sensitive</th> 27 <th style="width: 100px;" i18n>Sensitive</th>
@@ -54,7 +54,7 @@
54 </div> 54 </div>
55 <div class="table-video-text"> 55 <div class="table-video-text">
56 <div> 56 <div>
57 <my-global-icon i18n-title title="The video was blocked due to automatic blocking of new videos" *ngIf="videoBlock.type == 2" iconName="robot"></my-global-icon> 57 <my-global-icon i18n-title title="The video was blocked due to automatic blocking of new videos" *ngIf="videoBlock.type === 2" iconName="robot"></my-global-icon>
58 {{ videoBlock.video.name }} 58 {{ videoBlock.video.name }}
59 </div> 59 </div>
60 <div class="text-muted">by {{ videoBlock.video.channel?.displayName }} on {{ videoBlock.video.channel?.host }} </div> 60 <div class="text-muted">by {{ videoBlock.video.channel?.displayName }} on {{ videoBlock.video.channel?.host }} </div>
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
index adef16975..3edcb1c63 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
@@ -28,11 +28,11 @@ export class VideoBlockListComponent extends RestTable implements OnInit {
28 28
29 inputFilters: AdvancedInputFilter[] = [ 29 inputFilters: AdvancedInputFilter[] = [
30 { 30 {
31 queryParams: { 'search': 'type:auto' }, 31 queryParams: { search: 'type:auto' },
32 label: $localize`Automatic blocks` 32 label: $localize`Automatic blocks`
33 }, 33 },
34 { 34 {
35 queryParams: { 'search': 'type:manual' }, 35 queryParams: { search: 'type:manual' },
36 label: $localize`Manual blocks` 36 label: $localize`Manual blocks`
37 } 37 }
38 ] 38 ]
diff --git a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts
index 4904bcc25..512ceffd9 100644
--- a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts
+++ b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts
@@ -44,11 +44,11 @@ export class VideoCommentListComponent extends RestTable implements OnInit {
44 44
45 inputFilters: AdvancedInputFilter[] = [ 45 inputFilters: AdvancedInputFilter[] = [
46 { 46 {
47 queryParams: { 'search': 'local:true' }, 47 queryParams: { search: 'local:true' },
48 label: $localize`Local comments` 48 label: $localize`Local comments`
49 }, 49 },
50 { 50 {
51 queryParams: { 'search': 'local:false' }, 51 queryParams: { search: 'local:false' },
52 label: $localize`Remote comments` 52 label: $localize`Remote comments`
53 } 53 }
54 ] 54 ]
@@ -66,7 +66,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit {
66 private videoCommentService: VideoCommentService, 66 private videoCommentService: VideoCommentService,
67 private markdownRenderer: MarkdownService, 67 private markdownRenderer: MarkdownService,
68 private bulkService: BulkService 68 private bulkService: BulkService
69 ) { 69 ) {
70 super() 70 super()
71 71
72 this.videoCommentActions = [ 72 this.videoCommentActions = [
diff --git a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html
index 8d8f12c48..a41c7d700 100644
--- a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html
+++ b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html
@@ -3,7 +3,7 @@
3</div> 3</div>
4 4
5<div class="search-bar"> 5<div class="search-bar">
6 <input type="text" (input)="onSearchChange($event)" i18n-placeholder placeholder="Search..." autofocus /> 6 <input type="text" (input)="onSearchChange($event)" i18n-placeholder placeholder="Search..." myAutofocus />
7</div> 7</div>
8 8
9<div class="alert alert-info" i18n *ngIf="pluginInstalled"> 9<div class="alert alert-info" i18n *ngIf="pluginInstalled">
diff --git a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts
index 10fb52911..402bef1ea 100644
--- a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts
+++ b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts
@@ -103,8 +103,8 @@ export class PluginShowInstalledComponent extends FormReactive implements OnInit
103 const settingsValues: any = {} 103 const settingsValues: any = {}
104 104
105 for (const setting of this.registeredSettings) { 105 for (const setting of this.registeredSettings) {
106 buildOptions[ setting.name ] = null 106 buildOptions[setting.name] = null
107 settingsValues[ setting.name ] = this.getSetting(setting.name) 107 settingsValues[setting.name] = this.getSetting(setting.name)
108 } 108 }
109 109
110 this.buildForm(buildOptions) 110 this.buildForm(buildOptions)
@@ -117,7 +117,7 @@ export class PluginShowInstalledComponent extends FormReactive implements OnInit
117 private getSetting (name: string) { 117 private getSetting (name: string) {
118 const settings = this.plugin.settings 118 const settings = this.plugin.settings
119 119
120 if (settings && settings[name] !== undefined) return settings[name] 120 if (settings?.[name] !== undefined) return settings[name]
121 121
122 const registered = this.registeredSettings.find(r => r.name === name) 122 const registered = this.registeredSettings.find(r => r.name === name)
123 123
diff --git a/client/src/app/+admin/plugins/shared/plugin-api.service.ts b/client/src/app/+admin/plugins/shared/plugin-api.service.ts
index d91fccc09..c4f480cae 100644
--- a/client/src/app/+admin/plugins/shared/plugin-api.service.ts
+++ b/client/src/app/+admin/plugins/shared/plugin-api.service.ts
@@ -1,10 +1,8 @@
1import { Observable } from 'rxjs' 1import { catchError } from 'rxjs/operators'
2import { catchError, map, switchMap } from 'rxjs/operators'
3import { HttpClient, HttpParams } from '@angular/common/http' 2import { HttpClient, HttpParams } from '@angular/common/http'
4import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
5import { ComponentPagination, RestExtractor, RestService } from '@app/core' 4import { ComponentPagination, RestExtractor, RestService } from '@app/core'
6import { PluginService } from '@app/core/plugins/plugin.service' 5import { PluginService } from '@app/core/plugins/plugin.service'
7import { peertubeTranslate } from '@shared/core-utils/i18n'
8import { 6import {
9 InstallOrUpdatePlugin, 7 InstallOrUpdatePlugin,
10 ManagePlugin, 8 ManagePlugin,
diff --git a/client/src/app/+admin/system/jobs/job.service.ts b/client/src/app/+admin/system/jobs/job.service.ts
index 4b4a8914f..6c4a07469 100644
--- a/client/src/app/+admin/system/jobs/job.service.ts
+++ b/client/src/app/+admin/system/jobs/job.service.ts
@@ -20,9 +20,9 @@ export class JobService {
20 ) {} 20 ) {}
21 21
22 getJobs (options: { 22 getJobs (options: {
23 jobState?: JobStateClient, 23 jobState?: JobStateClient
24 jobType: JobTypeClient, 24 jobType: JobTypeClient
25 pagination: RestPagination, 25 pagination: RestPagination
26 sort: SortMeta 26 sort: SortMeta
27 }): Observable<ResultList<Job>> { 27 }): Observable<ResultList<Job>> {
28 const { jobState, jobType, pagination, sort } = options 28 const { jobState, jobType, pagination, sort } = options
@@ -32,7 +32,7 @@ export class JobService {
32 32
33 if (jobType !== 'all') params = params.append('jobType', jobType) 33 if (jobType !== 'all') params = params.append('jobType', jobType)
34 34
35 return this.authHttp.get<ResultList<Job>>(JobService.BASE_JOB_URL + `/${jobState ? jobState : ''}`, { params }) 35 return this.authHttp.get<ResultList<Job>>(JobService.BASE_JOB_URL + `/${jobState || ''}`, { params })
36 .pipe( 36 .pipe(
37 map(res => { 37 map(res => {
38 return this.restExtractor.convertResultListDateToHuman(res, [ 'createdAt', 'processedOn', 'finishedOn' ]) 38 return this.restExtractor.convertResultListDateToHuman(res, [ 'createdAt', 'processedOn', 'finishedOn' ])
diff --git a/client/src/app/+admin/system/logs/logs.service.ts b/client/src/app/+admin/system/logs/logs.service.ts
index 69439a179..0c222cad2 100644
--- a/client/src/app/+admin/system/logs/logs.service.ts
+++ b/client/src/app/+admin/system/logs/logs.service.ts
@@ -18,9 +18,9 @@ export class LogsService {
18 ) {} 18 ) {}
19 19
20 getLogs (options: { 20 getLogs (options: {
21 isAuditLog: boolean, 21 isAuditLog: boolean
22 startDate: string, 22 startDate: string
23 level?: LogLevel, 23 level?: LogLevel
24 endDate?: string 24 endDate?: string
25 }): Observable<any[]> { 25 }): Observable<any[]> {
26 const { isAuditLog, startDate } = options 26 const { isAuditLog, startDate } = options
diff --git a/client/src/app/+admin/users/user-edit/user-create.component.ts b/client/src/app/+admin/users/user-edit/user-create.component.ts
index 8403db91a..b61b22fd0 100644
--- a/client/src/app/+admin/users/user-edit/user-create.component.ts
+++ b/client/src/app/+admin/users/user-edit/user-create.component.ts
@@ -33,7 +33,7 @@ export class UserCreateComponent extends UserEdit implements OnInit {
33 private router: Router, 33 private router: Router,
34 private notifier: Notifier, 34 private notifier: Notifier,
35 private userService: UserService 35 private userService: UserService
36 ) { 36 ) {
37 super() 37 super()
38 38
39 this.buildQuotaOptions() 39 this.buildQuotaOptions()
@@ -78,7 +78,9 @@ export class UserCreateComponent extends UserEdit implements OnInit {
78 this.router.navigate([ '/admin/users/list' ]) 78 this.router.navigate([ '/admin/users/list' ])
79 }, 79 },
80 80
81 error: err => this.error = err.message 81 error: err => {
82 this.error = err.message
83 }
82 }) 84 })
83 } 85 }
84 86
diff --git a/client/src/app/+admin/users/user-edit/user-edit.ts b/client/src/app/+admin/users/user-edit/user-edit.ts
index ae1f79ba0..af5e674a7 100644
--- a/client/src/app/+admin/users/user-edit/user-edit.ts
+++ b/client/src/app/+admin/users/user-edit/user-edit.ts
@@ -7,7 +7,7 @@ import { HTMLServerConfig, UserAdminFlag, UserRole, VideoResolution } from '@sha
7import { SelectOptionsItem } from '../../../../types/select-options-item.model' 7import { SelectOptionsItem } from '../../../../types/select-options-item.model'
8 8
9@Directive() 9@Directive()
10// tslint:disable-next-line: directive-class-suffix 10// eslint-disable-next-line @angular-eslint/directive-class-suffix
11export abstract class UserEdit extends FormReactive implements OnInit { 11export abstract class UserEdit extends FormReactive implements OnInit {
12 videoQuotaOptions: SelectOptionsItem[] = [] 12 videoQuotaOptions: SelectOptionsItem[] = []
13 videoQuotaDailyOptions: SelectOptionsItem[] = [] 13 videoQuotaDailyOptions: SelectOptionsItem[] = []
diff --git a/client/src/app/+admin/users/user-edit/user-password.component.ts b/client/src/app/+admin/users/user-edit/user-password.component.ts
index 7c42b9241..42bf20de1 100644
--- a/client/src/app/+admin/users/user-edit/user-password.component.ts
+++ b/client/src/app/+admin/users/user-edit/user-password.component.ts
@@ -20,7 +20,7 @@ export class UserPasswordComponent extends FormReactive implements OnInit {
20 protected formValidatorService: FormValidatorService, 20 protected formValidatorService: FormValidatorService,
21 private notifier: Notifier, 21 private notifier: Notifier,
22 private userService: UserService 22 private userService: UserService
23 ) { 23 ) {
24 super() 24 super()
25 } 25 }
26 26
@@ -39,7 +39,9 @@ export class UserPasswordComponent extends FormReactive implements OnInit {
39 .subscribe({ 39 .subscribe({
40 next: () => this.notifier.success($localize`Password changed for user ${this.username}.`), 40 next: () => this.notifier.success($localize`Password changed for user ${this.username}.`),
41 41
42 error: err => this.error = err.message 42 error: err => {
43 this.error = err.message
44 }
43 }) 45 })
44 } 46 }
45 47
diff --git a/client/src/app/+admin/users/user-edit/user-update.component.ts b/client/src/app/+admin/users/user-edit/user-update.component.ts
index 2128ba4fd..42599a17e 100644
--- a/client/src/app/+admin/users/user-edit/user-update.component.ts
+++ b/client/src/app/+admin/users/user-edit/user-update.component.ts
@@ -33,7 +33,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
33 private router: Router, 33 private router: Router,
34 private notifier: Notifier, 34 private notifier: Notifier,
35 private userService: UserService 35 private userService: UserService
36 ) { 36 ) {
37 super() 37 super()
38 38
39 this.buildQuotaOptions() 39 this.buildQuotaOptions()
@@ -63,7 +63,9 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
63 .subscribe({ 63 .subscribe({
64 next: user => this.onUserFetched(user), 64 next: user => this.onUserFetched(user),
65 65
66 error: err => this.error = err.message 66 error: err => {
67 this.error = err.message
68 }
67 }) 69 })
68 }) 70 })
69 } 71 }
@@ -91,7 +93,9 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
91 this.router.navigate([ '/admin/users/list' ]) 93 this.router.navigate([ '/admin/users/list' ])
92 }, 94 },
93 95
94 error: err => this.error = err.message 96 error: err => {
97 this.error = err.message
98 }
95 }) 99 })
96 } 100 }
97 101
@@ -114,7 +118,9 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
114 this.notifier.success($localize`An email asking for password reset has been sent to ${this.user.username}.`) 118 this.notifier.success($localize`An email asking for password reset has been sent to ${this.user.username}.`)
115 }, 119 },
116 120
117 error: err => this.error = err.message 121 error: err => {
122 this.error = err.message
123 }
118 }) 124 })
119 } 125 }
120 126
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts
index d4406549a..39caf5ed5 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.ts
+++ b/client/src/app/+admin/users/user-list/user-list.component.ts
@@ -36,7 +36,7 @@ export class UserListComponent extends RestTable implements OnInit {
36 36
37 inputFilters: AdvancedInputFilter[] = [ 37 inputFilters: AdvancedInputFilter[] = [
38 { 38 {
39 queryParams: { 'search': 'banned:true' }, 39 queryParams: { search: 'banned:true' },
40 label: $localize`Banned users` 40 label: $localize`Banned users`
41 } 41 }
42 ] 42 ]
diff --git a/client/src/app/+login/login.component.html b/client/src/app/+login/login.component.html
index 27793ff0c..0be67368e 100644
--- a/client/src/app/+login/login.component.html
+++ b/client/src/app/+login/login.component.html
@@ -21,7 +21,7 @@
21 <label i18n for="username">User</label> 21 <label i18n for="username">User</label>
22 <input 22 <input
23 type="text" id="username" i18n-placeholder placeholder="Username or email address" required tabindex="1" 23 type="text" id="username" i18n-placeholder placeholder="Username or email address" required tabindex="1"
24 formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" autofocus 24 formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" myAutofocus
25 > 25 >
26 </div> 26 </div>
27 27
diff --git a/client/src/app/+login/login.component.ts b/client/src/app/+login/login.component.ts
index 16876afd6..1fa4bd3b5 100644
--- a/client/src/app/+login/login.component.ts
+++ b/client/src/app/+login/login.component.ts
@@ -46,7 +46,7 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
46 private redirectService: RedirectService, 46 private redirectService: RedirectService,
47 private notifier: Notifier, 47 private notifier: Notifier,
48 private hooks: HooksService 48 private hooks: HooksService
49 ) { 49 ) {
50 super() 50 super()
51 } 51 }
52 52
diff --git a/client/src/app/+my-account/my-account-applications/my-account-applications.component.ts b/client/src/app/+my-account/my-account-applications/my-account-applications.component.ts
index 6873c7d40..e88cdd228 100644
--- a/client/src/app/+my-account/my-account-applications/my-account-applications.component.ts
+++ b/client/src/app/+my-account/my-account-applications/my-account-applications.component.ts
@@ -36,6 +36,7 @@ export class MyAccountApplicationsComponent implements OnInit {
36 36
37 async renewToken () { 37 async renewToken () {
38 const res = await this.confirmService.confirm( 38 const res = await this.confirmService.confirm(
39 // eslint-disable-next-line max-len
39 $localize`Renewing the token will disallow previously configured clients from retrieving the feed until they use the new token. Proceed?`, 40 $localize`Renewing the token will disallow previously configured clients from retrieving the feed until they use the new token. Proceed?`,
40 $localize`Renew token` 41 $localize`Renew token`
41 ) 42 )
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
index 08bc5b425..9b87daa40 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
@@ -28,7 +28,7 @@ export class MyAccountChangeEmailComponent extends FormReactive implements OnIni
28 ngOnInit () { 28 ngOnInit () {
29 this.buildForm({ 29 this.buildForm({
30 'new-email': USER_EMAIL_VALIDATOR, 30 'new-email': USER_EMAIL_VALIDATOR,
31 'password': USER_PASSWORD_VALIDATOR 31 password: USER_PASSWORD_VALIDATOR
32 }) 32 })
33 33
34 this.user = this.authService.getUser() 34 this.user = this.authService.getUser()
@@ -38,8 +38,8 @@ export class MyAccountChangeEmailComponent extends FormReactive implements OnIni
38 this.error = null 38 this.error = null
39 this.success = null 39 this.success = null
40 40
41 const password = this.form.value[ 'password' ] 41 const password = this.form.value['password']
42 const email = this.form.value[ 'new-email' ] 42 const email = this.form.value['new-email']
43 43
44 forkJoin([ 44 forkJoin([
45 this.serverService.getConfig(), 45 this.serverService.getConfig(),
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts
index f91b2f37b..47e54dc23 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts
@@ -1,7 +1,11 @@
1import { filter } from 'rxjs/operators' 1import { filter } from 'rxjs/operators'
2import { Component, OnInit } from '@angular/core' 2import { Component, OnInit } from '@angular/core'
3import { AuthService, Notifier, UserService } from '@app/core' 3import { AuthService, Notifier, UserService } from '@app/core'
4import { USER_CONFIRM_PASSWORD_VALIDATOR, USER_PASSWORD_VALIDATOR, USER_EXISTING_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators' 4import {
5 USER_CONFIRM_PASSWORD_VALIDATOR,
6 USER_EXISTING_PASSWORD_VALIDATOR,
7 USER_PASSWORD_VALIDATOR
8} from '@app/shared/form-validators/user-validators'
5import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 9import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
6import { User } from '@shared/models' 10import { User } from '@shared/models'
7 11
@@ -19,7 +23,7 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
19 private notifier: Notifier, 23 private notifier: Notifier,
20 private authService: AuthService, 24 private authService: AuthService,
21 private userService: UserService 25 private userService: UserService
22 ) { 26 ) {
23 super() 27 super()
24 } 28 }
25 29
@@ -35,13 +39,13 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
35 const confirmPasswordControl = this.form.get('new-confirmed-password') 39 const confirmPasswordControl = this.form.get('new-confirmed-password')
36 40
37 confirmPasswordControl.valueChanges 41 confirmPasswordControl.valueChanges
38 .pipe(filter(v => v !== this.form.value[ 'new-password' ])) 42 .pipe(filter(v => v !== this.form.value['new-password']))
39 .subscribe(() => confirmPasswordControl.setErrors({ matchPassword: true })) 43 .subscribe(() => confirmPasswordControl.setErrors({ matchPassword: true }))
40 } 44 }
41 45
42 changePassword () { 46 changePassword () {
43 const currentPassword = this.form.value[ 'current-password' ] 47 const currentPassword = this.form.value['current-password']
44 const newPassword = this.form.value[ 'new-password' ] 48 const newPassword = this.form.value['new-password']
45 49
46 this.userService.changePassword(currentPassword, newPassword) 50 this.userService.changePassword(currentPassword, newPassword)
47 .subscribe({ 51 .subscribe({
diff --git a/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts b/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts
index 5005cb630..4a46f1ad9 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts
@@ -15,10 +15,11 @@ export class MyAccountDangerZoneComponent {
15 private userService: UserService, 15 private userService: UserService,
16 private confirmService: ConfirmService, 16 private confirmService: ConfirmService,
17 private redirectService: RedirectService 17 private redirectService: RedirectService
18 ) { } 18 ) { }
19 19
20 async deleteMe () { 20 async deleteMe () {
21 const res = await this.confirmService.confirmWithInput( 21 const res = await this.confirmService.confirmWithInput(
22 // eslint-disable-next-line max-len
22 $localize`Are you sure you want to delete your account? This will delete all your data, including channels, videos and comments. Content cached by other servers and other third-parties might make longer to be deleted.`, 23 $localize`Are you sure you want to delete your account? This will delete all your data, including channels, videos and comments. Content cached by other servers and other third-parties might make longer to be deleted.`,
23 $localize`Type your username to confirm`, 24 $localize`Type your username to confirm`,
24 this.user.username, 25 this.user.username,
diff --git a/client/src/app/+my-account/my-account-settings/my-account-settings.component.ts b/client/src/app/+my-account/my-account-settings/my-account-settings.component.ts
index fc7635f38..a5bcb6496 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-settings.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-settings.component.ts
@@ -19,7 +19,7 @@ export class MyAccountSettingsComponent implements OnInit, AfterViewChecked {
19 private userService: UserService, 19 private userService: UserService,
20 private authService: AuthService, 20 private authService: AuthService,
21 private notifier: Notifier 21 private notifier: Notifier
22 ) {} 22 ) {}
23 23
24 get userInformationLoaded () { 24 get userInformationLoaded () {
25 return this.authService.userInformationLoaded 25 return this.authService.userInformationLoaded
diff --git a/client/src/app/+my-account/my-account.component.ts b/client/src/app/+my-account/my-account.component.ts
index eaf8a72e9..450454ca2 100644
--- a/client/src/app/+my-account/my-account.component.ts
+++ b/client/src/app/+my-account/my-account.component.ts
@@ -13,7 +13,7 @@ export class MyAccountComponent implements OnInit {
13 13
14 constructor ( 14 constructor (
15 private screenService: ScreenService 15 private screenService: ScreenService
16 ) { } 16 ) { }
17 17
18 get isBroadcastMessageDisplayed () { 18 get isBroadcastMessageDisplayed () {
19 return this.screenService.isBroadcastMessageDisplayed 19 return this.screenService.isBroadcastMessageDisplayed
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
index d983aacd9..fd00720d8 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
@@ -31,7 +31,7 @@ export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements
31 private notifier: Notifier, 31 private notifier: Notifier,
32 private router: Router, 32 private router: Router,
33 private videoChannelService: VideoChannelService 33 private videoChannelService: VideoChannelService
34 ) { 34 ) {
35 super() 35 super()
36 } 36 }
37 37
@@ -64,7 +64,7 @@ export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements
64 this.authService.refreshUserInformation() 64 this.authService.refreshUserInformation()
65 65
66 this.notifier.success($localize`Video channel ${videoChannelCreate.displayName} created.`) 66 this.notifier.success($localize`Video channel ${videoChannelCreate.displayName} created.`)
67 this.router.navigate(['/my-library', 'video-channels']) 67 this.router.navigate([ '/my-library', 'video-channels' ])
68 }, 68 },
69 69
70 error: err => { 70 error: err => {
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts
index e8bfbf9ce..f9521b8b5 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts
@@ -66,7 +66,9 @@ export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements
66 }) 66 })
67 }, 67 },
68 68
69 error: err => this.error = err.message 69 error: err => {
70 this.error = err.message
71 }
70 }) 72 })
71 }) 73 })
72 } 74 }
@@ -96,7 +98,9 @@ export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements
96 this.router.navigate([ '/my-library', 'video-channels' ]) 98 this.router.navigate([ '/my-library', 'video-channels' ])
97 }, 99 },
98 100
99 error: err => this.error = err.message 101 error: err => {
102 this.error = err.message
103 }
100 }) 104 })
101 } 105 }
102 106
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
index 8b665fd58..303f783fd 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
@@ -121,16 +121,16 @@ channel with the same name (${videoChannel.name})!`,
121 display: false 121 display: false
122 }, 122 },
123 scales: { 123 scales: {
124 xAxes: [{ 124 xAxes: [ {
125 display: false 125 display: false
126 }], 126 } ],
127 yAxes: [{ 127 yAxes: [ {
128 display: false, 128 display: false,
129 ticks: { 129 ticks: {
130 min: Math.max(0, this.videoChannelsMinimumDailyViews - (3 * this.videoChannelsMaximumDailyViews / 100)), 130 min: Math.max(0, this.videoChannelsMinimumDailyViews - (3 * this.videoChannelsMaximumDailyViews / 100)),
131 max: Math.max(1, this.videoChannelsMaximumDailyViews) 131 max: Math.max(1, this.videoChannelsMaximumDailyViews)
132 } 132 }
133 }] 133 } ]
134 }, 134 },
135 layout: { 135 layout: {
136 padding: { 136 padding: {
diff --git a/client/src/app/+my-library/my-history/my-history.component.ts b/client/src/app/+my-library/my-history/my-history.component.ts
index fe6e86346..a72d22e1c 100644
--- a/client/src/app/+my-library/my-history/my-history.component.ts
+++ b/client/src/app/+my-library/my-history/my-history.component.ts
@@ -1,4 +1,3 @@
1import { Subject } from 'rxjs'
2import { tap } from 'rxjs/operators' 1import { tap } from 'rxjs/operators'
3import { Component, ComponentFactoryResolver, OnInit, ViewChild } from '@angular/core' 2import { Component, ComponentFactoryResolver, OnInit, ViewChild } from '@angular/core'
4import { ActivatedRoute, Router } from '@angular/router' 3import { ActivatedRoute, Router } from '@angular/router'
@@ -109,9 +108,9 @@ export class MyHistoryComponent implements OnInit, DisableForReuseHook {
109 this.userService.updateMyProfile({ videosHistoryEnabled: this.videosHistoryEnabled }) 108 this.userService.updateMyProfile({ videosHistoryEnabled: this.videosHistoryEnabled })
110 .subscribe({ 109 .subscribe({
111 next: () => { 110 next: () => {
112 const message = this.videosHistoryEnabled === true ? 111 const message = this.videosHistoryEnabled === true
113 $localize`Videos history is enabled` : 112 ? $localize`Videos history is enabled`
114 $localize`Videos history is disabled` 113 : $localize`Videos history is disabled`
115 114
116 this.notifier.success(message) 115 this.notifier.success(message)
117 116
diff --git a/client/src/app/+my-library/my-library.component.ts b/client/src/app/+my-library/my-library.component.ts
index 2ee3559a7..16a7f63e3 100644
--- a/client/src/app/+my-library/my-library.component.ts
+++ b/client/src/app/+my-library/my-library.component.ts
@@ -17,7 +17,7 @@ export class MyLibraryComponent implements OnInit {
17 private serverService: ServerService, 17 private serverService: ServerService,
18 private authService: AuthService, 18 private authService: AuthService,
19 private screenService: ScreenService 19 private screenService: ScreenService
20 ) { } 20 ) { }
21 21
22 get isBroadcastMessageDisplayed () { 22 get isBroadcastMessageDisplayed () {
23 return this.screenService.isBroadcastMessageDisplayed 23 return this.screenService.isBroadcastMessageDisplayed
diff --git a/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.ts b/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.ts
index b92377bca..764da2369 100644
--- a/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.ts
+++ b/client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.ts
@@ -29,7 +29,7 @@ export class MyAcceptOwnershipComponent extends FormReactive implements OnInit {
29 private notifier: Notifier, 29 private notifier: Notifier,
30 private authService: AuthService, 30 private authService: AuthService,
31 private modalService: NgbModal 31 private modalService: NgbModal
32 ) { 32 ) {
33 super() 33 super()
34 } 34 }
35 35
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts b/client/src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts
index 3e3c3c878..8bc78b2db 100644
--- a/client/src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts
+++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts
@@ -29,7 +29,7 @@ export class MyVideoPlaylistCreateComponent extends MyVideoPlaylistEdit implemen
29 private router: Router, 29 private router: Router,
30 private videoPlaylistService: VideoPlaylistService, 30 private videoPlaylistService: VideoPlaylistService,
31 private serverService: ServerService 31 private serverService: ServerService
32 ) { 32 ) {
33 super() 33 super()
34 } 34 }
35 35
@@ -78,7 +78,9 @@ export class MyVideoPlaylistCreateComponent extends MyVideoPlaylistEdit implemen
78 this.router.navigate([ '/my-library', 'video-playlists' ]) 78 this.router.navigate([ '/my-library', 'video-playlists' ])
79 }, 79 },
80 80
81 error: err => this.error = err.message 81 error: err => {
82 this.error = err.message
83 }
82 }) 84 })
83 } 85 }
84 86
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.ts b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.ts
index 6aff5dbd7..d6959a50e 100644
--- a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.ts
+++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.ts
@@ -57,7 +57,7 @@ export class MyVideoPlaylistElementsComponent implements OnInit, OnDestroy {
57 ] 57 ]
58 58
59 this.paramsSub = this.route.params.subscribe(routeParams => { 59 this.paramsSub = this.route.params.subscribe(routeParams => {
60 this.videoPlaylistId = routeParams[ 'videoPlaylistId' ] 60 this.videoPlaylistId = routeParams['videoPlaylistId']
61 this.loadElements() 61 this.loadElements()
62 62
63 this.loadPlaylistInfo() 63 this.loadPlaylistInfo()
@@ -145,8 +145,6 @@ export class MyVideoPlaylistElementsComponent implements OnInit, OnDestroy {
145 * we add a delay to prevent unwanted drag&drop. 145 * we add a delay to prevent unwanted drag&drop.
146 * 146 *
147 * @see {@link https://github.com/Chocobozzz/PeerTube/issues/2078} 147 * @see {@link https://github.com/Chocobozzz/PeerTube/issues/2078}
148 *
149 * @returns {null|number} Null for no delay, or a number in milliseconds.
150 */ 148 */
151 getDragStartDelay (): null | number { 149 getDragStartDelay (): null | number {
152 if (this.screenService.isInTouchScreen()) { 150 if (this.screenService.isInTouchScreen()) {
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-update.component.ts b/client/src/app/+my-library/my-video-playlists/my-video-playlist-update.component.ts
index a3f54279b..06ac3ad50 100644
--- a/client/src/app/+my-library/my-video-playlists/my-video-playlist-update.component.ts
+++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-update.component.ts
@@ -65,14 +65,16 @@ export class MyVideoPlaylistUpdateComponent extends MyVideoPlaylistEdit implemen
65 }) 65 })
66 ) 66 )
67 .subscribe({ 67 .subscribe({
68 next: ([ videoPlaylistToUpdate, videoPlaylistPrivacies]) => { 68 next: ([ videoPlaylistToUpdate, videoPlaylistPrivacies ]) => {
69 this.videoPlaylistToUpdate = videoPlaylistToUpdate 69 this.videoPlaylistToUpdate = videoPlaylistToUpdate
70 this.videoPlaylistPrivacies = videoPlaylistPrivacies 70 this.videoPlaylistPrivacies = videoPlaylistPrivacies
71 71
72 this.hydrateFormFromPlaylist() 72 this.hydrateFormFromPlaylist()
73 }, 73 },
74 74
75 error: err => this.error = err.message 75 error: err => {
76 this.error = err.message
77 }
76 }) 78 })
77 } 79 }
78 80
@@ -99,7 +101,9 @@ export class MyVideoPlaylistUpdateComponent extends MyVideoPlaylistEdit implemen
99 this.router.navigate([ '/my-library', 'video-playlists' ]) 101 this.router.navigate([ '/my-library', 'video-playlists' ])
100 }, 102 },
101 103
102 error: err => this.error = err.message 104 error: err => {
105 this.error = err.message
106 }
103 }) 107 })
104 } 108 }
105 109
diff --git a/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.ts b/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.ts
index 8c1f94bf3..960c9a4f7 100644
--- a/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.ts
+++ b/client/src/app/+my-library/my-videos/modals/video-change-ownership.component.ts
@@ -25,7 +25,7 @@ export class VideoChangeOwnershipComponent extends FormReactive implements OnIni
25 private notifier: Notifier, 25 private notifier: Notifier,
26 private userService: UserService, 26 private userService: UserService,
27 private modalService: NgbModal 27 private modalService: NgbModal
28 ) { 28 ) {
29 super() 29 super()
30 } 30 }
31 31
@@ -49,7 +49,9 @@ export class VideoChangeOwnershipComponent extends FormReactive implements OnIni
49 const query = event.query 49 const query = event.query
50 this.userService.autocomplete(query) 50 this.userService.autocomplete(query)
51 .subscribe({ 51 .subscribe({
52 next: usernames => this.usernamePropositions = usernames, 52 next: usernames => {
53 this.usernamePropositions = usernames
54 },
53 55
54 error: err => this.notifier.error(err.message) 56 error: err => this.notifier.error(err.message)
55 }) 57 })
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.ts b/client/src/app/+my-library/my-videos/my-videos.component.ts
index 4f9b71cc6..edb9392dd 100644
--- a/client/src/app/+my-library/my-videos/my-videos.component.ts
+++ b/client/src/app/+my-library/my-videos/my-videos.component.ts
@@ -49,7 +49,7 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
49 49
50 inputFilters: AdvancedInputFilter[] = [ 50 inputFilters: AdvancedInputFilter[] = [
51 { 51 {
52 queryParams: { 'search': 'isLive:true' }, 52 queryParams: { search: 'isLive:true' },
53 label: $localize`Only live videos` 53 label: $localize`Only live videos`
54 } 54 }
55 ] 55 ]
@@ -107,7 +107,7 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
107 107
108 async deleteSelectedVideos () { 108 async deleteSelectedVideos () {
109 const toDeleteVideosIds = Object.keys(this.selection) 109 const toDeleteVideosIds = Object.keys(this.selection)
110 .filter(k => this.selection[ k ] === true) 110 .filter(k => this.selection[k] === true)
111 .map(k => parseInt(k, 10)) 111 .map(k => parseInt(k, 10))
112 112
113 const res = await this.confirmService.confirm( 113 const res = await this.confirmService.confirm(
diff --git a/client/src/app/+remote-interaction/remote-interaction.component.ts b/client/src/app/+remote-interaction/remote-interaction.component.ts
index 293f7edad..775cc580c 100644
--- a/client/src/app/+remote-interaction/remote-interaction.component.ts
+++ b/client/src/app/+remote-interaction/remote-interaction.component.ts
@@ -7,7 +7,7 @@ import { SearchService } from '@app/shared/shared-search'
7@Component({ 7@Component({
8 selector: 'my-remote-interaction', 8 selector: 'my-remote-interaction',
9 templateUrl: './remote-interaction.component.html', 9 templateUrl: './remote-interaction.component.html',
10 styleUrls: ['./remote-interaction.component.scss'] 10 styleUrls: [ './remote-interaction.component.scss' ]
11}) 11})
12export class RemoteInteractionComponent implements OnInit { 12export class RemoteInteractionComponent implements OnInit {
13 error = '' 13 error = ''
diff --git a/client/src/app/+reset-password/reset-password.component.ts b/client/src/app/+reset-password/reset-password.component.ts
index a1c04147b..11c5110fd 100644
--- a/client/src/app/+reset-password/reset-password.component.ts
+++ b/client/src/app/+reset-password/reset-password.component.ts
@@ -21,7 +21,7 @@ export class ResetPasswordComponent extends FormReactive implements OnInit {
21 private notifier: Notifier, 21 private notifier: Notifier,
22 private router: Router, 22 private router: Router,
23 private route: ActivatedRoute 23 private route: ActivatedRoute
24 ) { 24 ) {
25 super() 25 super()
26 } 26 }
27 27
diff --git a/client/src/app/+signup/+register/custom-stepper.component.ts b/client/src/app/+signup/+register/custom-stepper.component.ts
index 5a80895f9..3b7ba40e8 100644
--- a/client/src/app/+signup/+register/custom-stepper.component.ts
+++ b/client/src/app/+signup/+register/custom-stepper.component.ts
@@ -1,5 +1,5 @@
1import { Component } from '@angular/core'
2import { CdkStep, CdkStepper } from '@angular/cdk/stepper' 1import { CdkStep, CdkStepper } from '@angular/cdk/stepper'
2import { Component } from '@angular/core'
3 3
4@Component({ 4@Component({
5 selector: 'my-custom-stepper', 5 selector: 'my-custom-stepper',
@@ -14,13 +14,13 @@ export class CustomStepperComponent extends CdkStepper {
14 } 14 }
15 15
16 isCompleted (step: CdkStep) { 16 isCompleted (step: CdkStep) {
17 return step.stepControl && step.stepControl.dirty && step.stepControl.valid 17 return step.stepControl?.dirty && step.stepControl.valid
18 } 18 }
19 19
20 isAccessible (index: number) { 20 isAccessible (index: number) {
21 const stepsCompletedMap = this.steps.map(step => this.isCompleted(step)) 21 const stepsCompletedMap = this.steps.map(step => this.isCompleted(step))
22 return index === 0 22 return index === 0
23 ? true 23 ? true
24 : stepsCompletedMap[ index - 1 ] 24 : stepsCompletedMap[index - 1]
25 } 25 }
26} 26}
diff --git a/client/src/app/+signup/+register/register.component.ts b/client/src/app/+signup/+register/register.component.ts
index 056442107..d8ac39c7c 100644
--- a/client/src/app/+signup/+register/register.component.ts
+++ b/client/src/app/+signup/+register/register.component.ts
@@ -49,8 +49,7 @@ export class RegisterComponent implements OnInit {
49 private authService: AuthService, 49 private authService: AuthService,
50 private userService: UserService, 50 private userService: UserService,
51 private hooks: HooksService 51 private hooks: HooksService
52 ) { 52 ) { }
53 }
54 53
55 get requiresEmailVerification () { 54 get requiresEmailVerification () {
56 return this.serverConfig.signup.requiresEmailVerification 55 return this.serverConfig.signup.requiresEmailVerification
@@ -138,11 +137,15 @@ export class RegisterComponent implements OnInit {
138 this.success = $localize`You are now logged in as ${body.username}!` 137 this.success = $localize`You are now logged in as ${body.username}!`
139 }, 138 },
140 139
141 error: err => this.error = err.message 140 error: err => {
141 this.error = err.message
142 }
142 }) 143 })
143 }, 144 },
144 145
145 error: err => this.error = err.message 146 error: err => {
147 this.error = err.message
148 }
146 }) 149 })
147 } 150 }
148} 151}
diff --git a/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts
index 457a0abe0..827ec7652 100644
--- a/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts
+++ b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts
@@ -20,7 +20,7 @@ export class VerifyAccountEmailComponent implements OnInit {
20 private authService: AuthService, 20 private authService: AuthService,
21 private notifier: Notifier, 21 private notifier: Notifier,
22 private route: ActivatedRoute 22 private route: ActivatedRoute
23 ) { 23 ) {
24 } 24 }
25 25
26 ngOnInit () { 26 ngOnInit () {
diff --git a/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts b/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts
index d83fc1324..ef8fd79b9 100644
--- a/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts
+++ b/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts
@@ -1,5 +1,5 @@
1import { forkJoin, Subscription } from 'rxjs' 1import { forkJoin, Subscription } from 'rxjs'
2import { first, tap } from 'rxjs/operators' 2import { first } from 'rxjs/operators'
3import { Component, ComponentFactoryResolver, OnDestroy, OnInit } from '@angular/core' 3import { Component, ComponentFactoryResolver, OnDestroy, OnInit } from '@angular/core'
4import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
5import { AuthService, ConfirmService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core' 5import { AuthService, ConfirmService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core'
diff --git a/client/src/app/+video-channels/video-channels.component.html b/client/src/app/+video-channels/video-channels.component.html
index b4d81fe39..064fbb6f5 100644
--- a/client/src/app/+video-channels/video-channels.component.html
+++ b/client/src/app/+video-channels/video-channels.component.html
@@ -114,7 +114,7 @@
114 <a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a> 114 <a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a>
115 </ng-template> 115 </ng-template>
116 116
117 <list-overflow [items]="links" [itemTemplate]="linkTemplate"></list-overflow> 117 <my-list-overflow [items]="links" [itemTemplate]="linkTemplate"></my-list-overflow>
118 </div> 118 </div>
119 119
120 <router-outlet></router-outlet> 120 <router-outlet></router-outlet>
diff --git a/client/src/app/+video-channels/video-channels.component.ts b/client/src/app/+video-channels/video-channels.component.ts
index 6479644f1..272fc41d9 100644
--- a/client/src/app/+video-channels/video-channels.component.ts
+++ b/client/src/app/+video-channels/video-channels.component.ts
@@ -44,7 +44,7 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
44 ngOnInit () { 44 ngOnInit () {
45 this.routeSub = this.route.params 45 this.routeSub = this.route.params
46 .pipe( 46 .pipe(
47 map(params => params[ 'videoChannelName' ]), 47 map(params => params['videoChannelName']),
48 distinctUntilChanged(), 48 distinctUntilChanged(),
49 switchMap(videoChannelName => this.videoChannelService.getVideoChannel(videoChannelName)), 49 switchMap(videoChannelName => this.videoChannelService.getVideoChannel(videoChannelName)),
50 catchError(err => this.restExtractor.redirectTo404IfNotFound(err, 'other', [ 50 catchError(err => this.restExtractor.redirectTo404IfNotFound(err, 'other', [
@@ -64,9 +64,9 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
64 64
65 this.hotkeys = [ 65 this.hotkeys = [
66 new Hotkey('S', (event: KeyboardEvent): boolean => { 66 new Hotkey('S', (event: KeyboardEvent): boolean => {
67 this.subscribeButton.subscribed ? 67 if (this.subscribeButton.subscribed) this.subscribeButton.unsubscribe()
68 this.subscribeButton.unsubscribe() : 68 else this.subscribeButton.subscribe()
69 this.subscribeButton.subscribe() 69
70 return false 70 return false
71 }, undefined, $localize`Subscribe to the account`) 71 }, undefined, $localize`Subscribe to the account`)
72 ] 72 ]
diff --git a/client/src/app/+videos/+video-edit/shared/i18n-primeng-calendar.service.ts b/client/src/app/+videos/+video-edit/shared/i18n-primeng-calendar.service.ts
index 34848b036..4b201ac74 100644
--- a/client/src/app/+videos/+video-edit/shared/i18n-primeng-calendar.service.ts
+++ b/client/src/app/+videos/+video-edit/shared/i18n-primeng-calendar.service.ts
@@ -73,7 +73,7 @@ export class I18nPrimengCalendarService {
73 } 73 }
74 74
75 getTimezone () { 75 getTimezone () {
76 const gmt = new Date().toString().match(/([A-Z]+[\+-][0-9]+)/)[1] 76 const gmt = new Date().toString().match(/([A-Z]+[+-][0-9]+)/)[1]
77 const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone 77 const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
78 78
79 return `${timezone} - ${gmt}` 79 return `${timezone} - ${gmt}`
diff --git a/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.ts b/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.ts
index 875911b91..98d66ff00 100644
--- a/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.ts
+++ b/client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.ts
@@ -66,18 +66,18 @@ export class VideoCaptionAddModalComponent extends FormReactive implements OnIni
66 isReplacingExistingCaption () { 66 isReplacingExistingCaption () {
67 if (this.closingModal === true) return false 67 if (this.closingModal === true) return false
68 68
69 const languageId = this.form.value[ 'language' ] 69 const languageId = this.form.value['language']
70 70
71 return languageId && this.existingCaptions.indexOf(languageId) !== -1 71 return languageId && this.existingCaptions.includes(languageId)
72 } 72 }
73 73
74 async addCaption () { 74 async addCaption () {
75 const languageId = this.form.value[ 'language' ] 75 const languageId = this.form.value['language']
76 const languageObject = this.videoCaptionLanguages.find(l => l.id === languageId) 76 const languageObject = this.videoCaptionLanguages.find(l => l.id === languageId)
77 77
78 this.captionAdded.emit({ 78 this.captionAdded.emit({
79 language: languageObject, 79 language: languageObject,
80 captionfile: this.form.value[ 'captionfile' ] 80 captionfile: this.form.value['captionfile']
81 }) 81 })
82 82
83 this.hide() 83 this.hide()
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts b/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts
index 3a7dbed36..db1ef8d73 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts
+++ b/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts
@@ -24,7 +24,7 @@ function hydrateFormFromVideo (formGroup: FormGroup, video: VideoEdit, thumbnail
24 .then(response => response.blob()) 24 .then(response => response.blob())
25 .then(data => { 25 .then(data => {
26 formGroup.patchValue({ 26 formGroup.patchValue({
27 [ obj.name ]: data 27 [obj.name]: data
28 }) 28 })
29 }) 29 })
30 } 30 }
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
index 90a0e8f52..366c93a79 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
@@ -233,7 +233,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
233 233
234 async deleteCaption (caption: VideoCaptionEdit) { 234 async deleteCaption (caption: VideoCaptionEdit) {
235 // Caption recovers his former state 235 // Caption recovers his former state
236 if (caption.action && this.initialVideoCaptions.indexOf(caption.language.id) !== -1) { 236 if (caption.action && this.initialVideoCaptions.includes(caption.language.id)) {
237 caption.action = undefined 237 caption.action = undefined
238 return 238 return
239 } 239 }
@@ -297,7 +297,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
297 297
298 private trackPrivacyChange () { 298 private trackPrivacyChange () {
299 // We will update the schedule input and the wait transcoding checkbox validators 299 // We will update the schedule input and the wait transcoding checkbox validators
300 this.form.controls[ 'privacy' ] 300 this.form.controls['privacy']
301 .valueChanges 301 .valueChanges
302 .pipe(map(res => parseInt(res.toString(), 10))) 302 .pipe(map(res => parseInt(res.toString(), 10)))
303 .subscribe( 303 .subscribe(
@@ -336,12 +336,12 @@ export class VideoEditComponent implements OnInit, OnDestroy {
336 336
337 private trackChannelChange () { 337 private trackChannelChange () {
338 // We will update the "support" field depending on the channel 338 // We will update the "support" field depending on the channel
339 this.form.controls[ 'channelId' ] 339 this.form.controls['channelId']
340 .valueChanges 340 .valueChanges
341 .pipe(map(res => parseInt(res.toString(), 10))) 341 .pipe(map(res => parseInt(res.toString(), 10)))
342 .subscribe( 342 .subscribe(
343 newChannelId => { 343 newChannelId => {
344 const oldChannelId = parseInt(this.form.value[ 'channelId' ], 10) 344 const oldChannelId = parseInt(this.form.value['channelId'], 10)
345 345
346 // Not initialized yet 346 // Not initialized yet
347 if (isNaN(newChannelId)) return 347 if (isNaN(newChannelId)) return
@@ -350,7 +350,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
350 350
351 // Wait support field update 351 // Wait support field update
352 setTimeout(() => { 352 setTimeout(() => {
353 const currentSupport = this.form.value[ 'support' ] 353 const currentSupport = this.form.value['support']
354 354
355 // First time we set the channel? 355 // First time we set the channel?
356 if (isNaN(oldChannelId)) { 356 if (isNaN(oldChannelId)) {
diff --git a/client/src/app/+videos/+video-edit/video-add-components/drag-drop.directive.ts b/client/src/app/+videos/+video-edit/video-add-components/drag-drop.directive.ts
index 7b1a38c62..7c35e6b84 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/drag-drop.directive.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/drag-drop.directive.ts
@@ -1,26 +1,26 @@
1import { Directive, Output, EventEmitter, HostBinding, HostListener } from '@angular/core' 1import { Directive, Output, EventEmitter, HostBinding, HostListener } from '@angular/core'
2 2
3@Directive({ 3@Directive({
4 selector: '[dragDrop]' 4 selector: '[myDragDrop]'
5}) 5})
6export class DragDropDirective { 6export class DragDropDirective {
7 @Output() fileDropped = new EventEmitter<FileList>() 7 @Output() fileDropped = new EventEmitter<FileList>()
8 8
9 @HostBinding('class.dragover') dragover = false 9 @HostBinding('class.dragover') dragover = false
10 10
11 @HostListener('dragover', ['$event']) onDragOver (e: Event) { 11 @HostListener('dragover', [ '$event' ]) onDragOver (e: Event) {
12 e.preventDefault() 12 e.preventDefault()
13 e.stopPropagation() 13 e.stopPropagation()
14 this.dragover = true 14 this.dragover = true
15 } 15 }
16 16
17 @HostListener('dragleave', ['$event']) public onDragLeave (e: Event) { 17 @HostListener('dragleave', [ '$event' ]) public onDragLeave (e: Event) {
18 e.preventDefault() 18 e.preventDefault()
19 e.stopPropagation() 19 e.stopPropagation()
20 this.dragover = false 20 this.dragover = false
21 } 21 }
22 22
23 @HostListener('drop', ['$event']) public ondrop (e: DragEvent) { 23 @HostListener('drop', [ '$event' ]) public ondrop (e: DragEvent) {
24 e.preventDefault() 24 e.preventDefault()
25 e.stopPropagation() 25 e.stopPropagation()
26 this.dragover = false 26 this.dragover = false
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
index 30c79594d..1b9447e03 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
@@ -41,7 +41,7 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterView
41 private liveVideoService: LiveVideoService, 41 private liveVideoService: LiveVideoService,
42 private router: Router, 42 private router: Router,
43 private hooks: HooksService 43 private hooks: HooksService
44 ) { 44 ) {
45 super() 45 super()
46 } 46 }
47 47
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.html b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.html
index 20a7538db..0f1a94c84 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.html
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.html
@@ -1,4 +1,4 @@
1<div *ngIf="!hasImportedVideo" class="upload-video-container" dragDrop (fileDropped)="setTorrentFile($event)"> 1<div *ngIf="!hasImportedVideo" class="upload-video-container" myDragDrop (fileDropped)="setTorrentFile($event)">
2 <div class="first-step-block"> 2 <div class="first-step-block">
3 <my-global-icon class="upload-icon" iconName="upload" aria-hidden="true"></my-global-icon> 3 <my-global-icon class="upload-icon" iconName="upload" aria-hidden="true"></my-global-icon>
4 4
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
index fef1f5d65..87e47683f 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
@@ -5,7 +5,7 @@ import { scrollToTop } from '@app/helpers'
5import { FormValidatorService } from '@app/shared/shared-forms' 5import { FormValidatorService } from '@app/shared/shared-forms'
6import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' 6import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
7import { LoadingBarService } from '@ngx-loading-bar/core' 7import { LoadingBarService } from '@ngx-loading-bar/core'
8import { PeerTubeProblemDocument, ServerErrorCode, VideoPrivacy, VideoUpdate } from '@shared/models' 8import { PeerTubeProblemDocument, ServerErrorCode, VideoUpdate } from '@shared/models'
9import { hydrateFormFromVideo } from '../shared/video-edit-utils' 9import { hydrateFormFromVideo } from '../shared/video-edit-utils'
10import { VideoSend } from './video-send' 10import { VideoSend } from './video-send'
11 11
@@ -43,7 +43,7 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Af
43 private router: Router, 43 private router: Router,
44 private videoImportService: VideoImportService, 44 private videoImportService: VideoImportService,
45 private hooks: HooksService 45 private hooks: HooksService
46 ) { 46 ) {
47 super() 47 super()
48 } 48 }
49 49
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
index e1893b28f..3487c1adf 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
@@ -59,7 +59,7 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, AfterV
59 } 59 }
60 60
61 isTargetUrlValid () { 61 isTargetUrlValid () {
62 return this.targetUrl && this.targetUrl.match(/https?:\/\//) 62 return this.targetUrl?.match(/https?:\/\//)
63 } 63 }
64 64
65 importVideo () { 65 importVideo () {
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-send.ts b/client/src/app/+videos/+video-edit/video-add-components/video-send.ts
index ce8de049d..efa8c85a3 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-send.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-send.ts
@@ -9,7 +9,7 @@ import { LoadingBarService } from '@ngx-loading-bar/core'
9import { HTMLServerConfig, VideoConstant, VideoPrivacy } from '@shared/models' 9import { HTMLServerConfig, VideoConstant, VideoPrivacy } from '@shared/models'
10 10
11@Directive() 11@Directive()
12// tslint:disable-next-line: directive-class-suffix 12// eslint-disable-next-line @angular-eslint/directive-class-suffix
13export abstract class VideoSend extends FormReactive implements OnInit { 13export abstract class VideoSend extends FormReactive implements OnInit {
14 userVideoChannels: SelectChannelItem[] = [] 14 userVideoChannels: SelectChannelItem[] = []
15 videoPrivacies: VideoConstant<VideoPrivacy>[] = [] 15 videoPrivacies: VideoConstant<VideoPrivacy>[] = []
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.html b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.html
index 14cd06fcf..db494a02f 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.html
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.html
@@ -1,4 +1,4 @@
1<div *ngIf="!isUploadingVideo" class="upload-video-container" dragDrop (fileDropped)="onFileDropped($event)"> 1<div *ngIf="!isUploadingVideo" class="upload-video-container" myDragDrop (fileDropped)="onFileDropped($event)">
2 <div class="first-step-block"> 2 <div class="first-step-block">
3 <my-global-icon class="upload-icon" iconName="upload" aria-hidden="true"></my-global-icon> 3 <my-global-icon class="upload-icon" iconName="upload" aria-hidden="true"></my-global-icon>
4 4
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
index b8cb4fa1e..dee2bb57a 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
@@ -128,7 +128,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
128 128
129 onUploadVideoOngoing (state: UploadState) { 129 onUploadVideoOngoing (state: UploadState) {
130 switch (state.status) { 130 switch (state.status) {
131 case 'error': 131 case 'error': {
132 const error = state.response?.error || 'Unknow error' 132 const error = state.response?.error || 'Unknow error'
133 133
134 this.handleUploadError({ 134 this.handleUploadError({
@@ -143,6 +143,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
143 url: state.url 143 url: state.url
144 }) 144 })
145 break 145 break
146 }
146 147
147 case 'cancelled': 148 case 'cancelled':
148 this.isUploadingVideo = false 149 this.isUploadingVideo = false
@@ -323,6 +324,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
323 const videoQuotaUsedBytes = bytePipes.transform(this.userVideoQuotaUsed, 0) 324 const videoQuotaUsedBytes = bytePipes.transform(this.userVideoQuotaUsed, 0)
324 const videoQuotaBytes = bytePipes.transform(videoQuota, 0) 325 const videoQuotaBytes = bytePipes.transform(videoQuota, 0)
325 326
327 // eslint-disable-next-line max-len
326 const msg = $localize`Your video quota is exceeded with this video (video size: ${videoSizeBytes}, used: ${videoQuotaUsedBytes}, quota: ${videoQuotaBytes})` 328 const msg = $localize`Your video quota is exceeded with this video (video size: ${videoSizeBytes}, used: ${videoQuotaUsedBytes}, quota: ${videoQuotaBytes})`
327 this.notifier.error(msg) 329 this.notifier.error(msg)
328 330
@@ -341,6 +343,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
341 const videoSizeBytes = bytePipes.transform(videofile.size, 0) 343 const videoSizeBytes = bytePipes.transform(videofile.size, 0)
342 const quotaUsedDailyBytes = bytePipes.transform(this.userVideoQuotaUsedDaily, 0) 344 const quotaUsedDailyBytes = bytePipes.transform(this.userVideoQuotaUsedDaily, 0)
343 const quotaDailyBytes = bytePipes.transform(videoQuotaDaily, 0) 345 const quotaDailyBytes = bytePipes.transform(videoQuotaDaily, 0)
346 // eslint-disable-next-line max-len
344 const msg = $localize`Your daily video quota is exceeded with this video (video size: ${videoSizeBytes}, used: ${quotaUsedDailyBytes}, quota: ${quotaDailyBytes})` 347 const msg = $localize`Your daily video quota is exceeded with this video (video size: ${videoSizeBytes}, used: ${quotaUsedDailyBytes}, quota: ${quotaDailyBytes})`
345 this.notifier.error(msg) 348 this.notifier.error(msg)
346 349
diff --git a/client/src/app/+videos/+video-edit/video-update.component.ts b/client/src/app/+videos/+video-edit/video-update.component.ts
index 95336dc75..9bef60133 100644
--- a/client/src/app/+videos/+video-edit/video-update.component.ts
+++ b/client/src/app/+videos/+video-edit/video-update.component.ts
@@ -38,7 +38,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
38 private loadingBar: LoadingBarService, 38 private loadingBar: LoadingBarService,
39 private videoCaptionService: VideoCaptionService, 39 private videoCaptionService: VideoCaptionService,
40 private liveVideoService: LiveVideoService 40 private liveVideoService: LiveVideoService
41 ) { 41 ) {
42 super() 42 super()
43 } 43 }
44 44
@@ -119,8 +119,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
119 } 119 }
120 120
121 update () { 121 update () {
122 if (this.checkForm() === false 122 if (this.checkForm() === false || this.isUpdatingVideo === true) {
123 || this.isUpdatingVideo === true) {
124 return 123 return
125 } 124 }
126 125
diff --git a/client/src/app/+videos/+video-edit/video-update.resolver.ts b/client/src/app/+videos/+video-edit/video-update.resolver.ts
index 9172b78a8..91e76b7fe 100644
--- a/client/src/app/+videos/+video-edit/video-update.resolver.ts
+++ b/client/src/app/+videos/+video-edit/video-update.resolver.ts
@@ -18,7 +18,7 @@ export class VideoUpdateResolver implements Resolve<any> {
18 } 18 }
19 19
20 resolve (route: ActivatedRouteSnapshot) { 20 resolve (route: ActivatedRouteSnapshot) {
21 const uuid: string = route.params[ 'uuid' ] 21 const uuid: string = route.params['uuid']
22 22
23 return this.videoService.getVideo({ videoId: uuid }) 23 return this.videoService.getVideo({ videoId: uuid })
24 .pipe( 24 .pipe(
@@ -42,8 +42,8 @@ export class VideoUpdateResolver implements Resolve<any> {
42 ), 42 ),
43 43
44 video.isLive 44 video.isLive
45 ? this.liveVideoService.getVideoLive(video.id) 45 ? this.liveVideoService.getVideoLive(video.id)
46 : of(undefined) 46 : of(undefined)
47 ] 47 ]
48 } 48 }
49} 49}
diff --git a/client/src/app/+videos/+video-watch/player-styles.component.ts b/client/src/app/+videos/+video-watch/player-styles.component.ts
index 9b1672a8c..448a0ee96 100644
--- a/client/src/app/+videos/+video-watch/player-styles.component.ts
+++ b/client/src/app/+videos/+video-watch/player-styles.component.ts
@@ -8,7 +8,7 @@ import { Component, ViewEncapsulation } from '@angular/core'
8 selector: 'my-player-styles', 8 selector: 'my-player-styles',
9 template: '', 9 template: '',
10 styleUrls: [ './player-styles.component.scss' ], 10 styleUrls: [ './player-styles.component.scss' ],
11 // tslint:disable:use-component-view-encapsulation 11 /* eslint-disable @angular-eslint/use-component-view-encapsulation */
12 encapsulation: ViewEncapsulation.None 12 encapsulation: ViewEncapsulation.None
13}) 13})
14export class PlayerStylesComponent { 14export class PlayerStylesComponent {
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts b/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts
index ac65f7260..71fb127f6 100644
--- a/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts
@@ -25,7 +25,7 @@ import { VideoCommentCreate } from '@shared/models'
25@Component({ 25@Component({
26 selector: 'my-video-comment-add', 26 selector: 'my-video-comment-add',
27 templateUrl: './video-comment-add.component.html', 27 templateUrl: './video-comment-add.component.html',
28 styleUrls: ['./video-comment-add.component.scss'] 28 styleUrls: [ './video-comment-add.component.scss' ]
29}) 29})
30export class VideoCommentAddComponent extends FormReactive implements OnChanges, OnInit { 30export class VideoCommentAddComponent extends FormReactive implements OnChanges, OnInit {
31 @Input() user: User 31 @Input() user: User
@@ -64,7 +64,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnChanges,
64 for (const emojiMarkupName in emojiMarkupObjectList) { 64 for (const emojiMarkupName in emojiMarkupObjectList) {
65 if (emojiMarkupName) { 65 if (emojiMarkupName) {
66 const emoji = emojiMarkupObjectList[emojiMarkupName] 66 const emoji = emojiMarkupObjectList[emojiMarkupName]
67 emojiMarkupArrayList.push([emoji, emojiMarkupName]) 67 emojiMarkupArrayList.push([ emoji, emojiMarkupName ])
68 } 68 }
69 } 69 }
70 70
@@ -91,7 +91,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnChanges,
91 // Not initialized yet 91 // Not initialized yet
92 if (!this.form) return 92 if (!this.form) return
93 93
94 if (changes.textValue && changes.textValue.currentValue && changes.textValue.currentValue !== changes.textValue.previousValue) { 94 if (changes.textValue?.currentValue && changes.textValue.currentValue !== changes.textValue.previousValue) {
95 this.patchTextValue(changes.textValue.currentValue, true) 95 this.patchTextValue(changes.textValue.currentValue, true)
96 } 96 }
97 } 97 }
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.html b/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.html
index d8b944b35..d0e9bcd29 100644
--- a/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.html
+++ b/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.html
@@ -29,7 +29,7 @@
29 class="comment-html" 29 class="comment-html"
30 [innerHTML]="sanitizedCommentHTML" 30 [innerHTML]="sanitizedCommentHTML"
31 (timestampClicked)="handleTimestampClicked($event)" 31 (timestampClicked)="handleTimestampClicked($event)"
32 timestampRouteTransformer 32 myTimestampRouteTransformer
33 ></div> 33 ></div>
34 34
35 <div class="comment-actions"> 35 <div class="comment-actions">
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts b/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts
index f858f4750..5bbcffe82 100644
--- a/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts
@@ -10,7 +10,7 @@ import { User, UserRight } from '@shared/models'
10@Component({ 10@Component({
11 selector: 'my-video-comment', 11 selector: 'my-video-comment',
12 templateUrl: './video-comment.component.html', 12 templateUrl: './video-comment.component.html',
13 styleUrls: ['./video-comment.component.scss'] 13 styleUrls: [ './video-comment.component.scss' ]
14}) 14})
15export class VideoCommentComponent implements OnInit, OnChanges { 15export class VideoCommentComponent implements OnInit, OnChanges {
16 @ViewChild('commentReportModal') commentReportModal: CommentReportComponent 16 @ViewChild('commentReportModal') commentReportModal: CommentReportComponent
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts
index 72866b874..17e0af3bc 100644
--- a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts
@@ -9,7 +9,7 @@ import { VideoComment, VideoCommentService, VideoCommentThreadTree } from '@app/
9@Component({ 9@Component({
10 selector: 'my-video-comments', 10 selector: 'my-video-comments',
11 templateUrl: './video-comments.component.html', 11 templateUrl: './video-comments.component.html',
12 styleUrls: ['./video-comments.component.scss'] 12 styleUrls: [ './video-comments.component.scss' ]
13}) 13})
14export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy { 14export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
15 @ViewChild('commentHighlightBlock') commentHighlightBlock: ElementRef 15 @ViewChild('commentHighlightBlock') commentHighlightBlock: ElementRef
@@ -200,7 +200,11 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
200 } 200 }
201 201
202 async onWantedToRedraft (commentToRedraft: VideoComment) { 202 async onWantedToRedraft (commentToRedraft: VideoComment) {
203 const confirm = await this.onWantedToDelete(commentToRedraft, $localize`Delete and re-draft`, $localize`Do you really want to delete and re-draft this comment?`) 203 const confirm = await this.onWantedToDelete(
204 commentToRedraft,
205 $localize`Delete and re-draft`,
206 $localize`Do you really want to delete and re-draft this comment?`
207 )
204 208
205 if (confirm) { 209 if (confirm) {
206 this.inReplyToCommentId = commentToRedraft.inReplyToCommentId 210 this.inReplyToCommentId = commentToRedraft.inReplyToCommentId
diff --git a/client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts b/client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts
index 0072492ac..257d463b4 100644
--- a/client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts
@@ -23,7 +23,7 @@ export class VideoAlertComponent {
23 } 23 }
24 24
25 hasVideoScheduledPublication () { 25 hasVideoScheduledPublication () {
26 return this.video && this.video.scheduledUpdate !== undefined 26 return this.video?.scheduledUpdate !== undefined
27 } 27 }
28 28
29 isWaitingForLive () { 29 isWaitingForLive () {
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
index 57f682899..2cfaad8f6 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
@@ -3,7 +3,7 @@
3 class="video-info-description-html" 3 class="video-info-description-html"
4 [innerHTML]="videoHTMLDescription" 4 [innerHTML]="videoHTMLDescription"
5 (timestampClicked)="onTimestampClicked($event)" 5 (timestampClicked)="onTimestampClicked($event)"
6 timestampRouteTransformer 6 myTimestampRouteTransformer
7 ></div> 7 ></div>
8 8
9 <div class="video-info-description-more" *ngIf="completeDescriptionShown === false && video.description?.length >= 250" (click)="showMoreDescription()"> 9 <div class="video-info-description-more" *ngIf="completeDescriptionShown === false && video.description?.length >= 250" (click)="showMoreDescription()">
diff --git a/client/src/app/+videos/+video-watch/shared/playlist/video-watch-playlist.component.ts b/client/src/app/+videos/+video-watch/shared/playlist/video-watch-playlist.component.ts
index f0f7966b1..78b3af4a7 100644
--- a/client/src/app/+videos/+video-watch/shared/playlist/video-watch-playlist.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/playlist/video-watch-playlist.component.ts
@@ -39,7 +39,7 @@ export class VideoWatchPlaylistComponent {
39 private notifier: Notifier, 39 private notifier: Notifier,
40 private videoPlaylist: VideoPlaylistService, 40 private videoPlaylist: VideoPlaylistService,
41 private localStorageService: LocalStorageService, 41 private localStorageService: LocalStorageService,
42 private sessionStorageService: SessionStorageService, 42 private sessionStorage: SessionStorageService,
43 private router: Router 43 private router: Router
44 ) { 44 ) {
45 // defaults to true 45 // defaults to true
@@ -50,7 +50,7 @@ export class VideoWatchPlaylistComponent {
50 this.setAutoPlayNextVideoPlaylistSwitchText() 50 this.setAutoPlayNextVideoPlaylistSwitchText()
51 51
52 // defaults to false 52 // defaults to false
53 this.loopPlaylist = this.sessionStorageService.getItem(VideoWatchPlaylistComponent.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST) === 'true' 53 this.loopPlaylist = this.sessionStorage.getItem(VideoWatchPlaylistComponent.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST) === 'true'
54 this.setLoopPlaylistSwitchText() 54 this.setLoopPlaylistSwitchText()
55 } 55 }
56 56
@@ -145,7 +145,7 @@ export class VideoWatchPlaylistComponent {
145 145
146 const start = previous.startTimestamp 146 const start = previous.startTimestamp
147 const stop = previous.stopTimestamp 147 const stop = previous.stopTimestamp
148 this.router.navigate([],{ queryParams: { playlistPosition: previous.position, start, stop } }) 148 this.router.navigate([], { queryParams: { playlistPosition: previous.position, start, stop } })
149 } 149 }
150 150
151 findPlaylistVideo (position: number, type: 'previous' | 'next'): VideoPlaylistElement { 151 findPlaylistVideo (position: number, type: 'previous' | 'next'): VideoPlaylistElement {
@@ -163,7 +163,7 @@ export class VideoWatchPlaylistComponent {
163 } 163 }
164 164
165 const found = this.playlistElements.find(e => e.position === position) 165 const found = this.playlistElements.find(e => e.position === position)
166 if (found && found.video) return found 166 if (found?.video) return found
167 167
168 const newPosition = type === 'previous' 168 const newPosition = type === 'previous'
169 ? position - 1 169 ? position - 1
@@ -178,7 +178,7 @@ export class VideoWatchPlaylistComponent {
178 178
179 const start = next.startTimestamp 179 const start = next.startTimestamp
180 const stop = next.stopTimestamp 180 const stop = next.stopTimestamp
181 this.router.navigate([],{ queryParams: { playlistPosition: next.position, start, stop } }) 181 this.router.navigate([], { queryParams: { playlistPosition: next.position, start, stop } })
182 } 182 }
183 183
184 switchAutoPlayNextVideoPlaylist () { 184 switchAutoPlayNextVideoPlaylist () {
diff --git a/client/src/app/+videos/+video-watch/shared/recommendations/recommended-videos.component.html b/client/src/app/+videos/+video-watch/shared/recommendations/recommended-videos.component.html
index e1040fead..bbfcab2ae 100644
--- a/client/src/app/+videos/+video-watch/shared/recommendations/recommended-videos.component.html
+++ b/client/src/app/+videos/+video-watch/shared/recommendations/recommended-videos.component.html
@@ -20,7 +20,7 @@
20 > 20 >
21 </my-video-miniature> 21 </my-video-miniature>
22 22
23 <hr *ngIf="!playlist && i == 0 && length > 1" /> 23 <hr *ngIf="!playlist && i === 0 && length > 1" />
24 </ng-container> 24 </ng-container>
25 </ng-container> 25 </ng-container>
26</div> 26</div>
diff --git a/client/src/app/+videos/+video-watch/shared/recommendations/recommended-videos.component.ts b/client/src/app/+videos/+video-watch/shared/recommendations/recommended-videos.component.ts
index 7f3703c08..dfc296d15 100644
--- a/client/src/app/+videos/+video-watch/shared/recommendations/recommended-videos.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/recommendations/recommended-videos.component.ts
@@ -51,7 +51,7 @@ export class RecommendedVideosComponent implements OnInit, OnChanges {
51 } else { 51 } else {
52 this.autoPlayNextVideo = this.sessionStorageService.getItem(UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO) === 'true' 52 this.autoPlayNextVideo = this.sessionStorageService.getItem(UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO) === 'true'
53 53
54 this.sessionStorageService.watch([UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO]).subscribe( 54 this.sessionStorageService.watch([ UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO ]).subscribe(
55 () => { 55 () => {
56 this.autoPlayNextVideo = this.sessionStorageService.getItem(UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO) === 'true' 56 this.autoPlayNextVideo = this.sessionStorageService.getItem(UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO) === 'true'
57 } 57 }
diff --git a/client/src/app/+videos/+video-watch/shared/timestamp-route-transformer.directive.ts b/client/src/app/+videos/+video-watch/shared/timestamp-route-transformer.directive.ts
index 45e023695..91fe5bf5d 100644
--- a/client/src/app/+videos/+video-watch/shared/timestamp-route-transformer.directive.ts
+++ b/client/src/app/+videos/+video-watch/shared/timestamp-route-transformer.directive.ts
@@ -1,12 +1,12 @@
1import { Directive, EventEmitter, HostListener, Output } from '@angular/core' 1import { Directive, EventEmitter, HostListener, Output } from '@angular/core'
2 2
3@Directive({ 3@Directive({
4 selector: '[timestampRouteTransformer]' 4 selector: '[myTimestampRouteTransformer]'
5}) 5})
6export class TimestampRouteTransformerDirective { 6export class TimestampRouteTransformerDirective {
7 @Output() timestampClicked = new EventEmitter<number>() 7 @Output() timestampClicked = new EventEmitter<number>()
8 8
9 @HostListener('click', ['$event']) 9 @HostListener('click', [ '$event' ])
10 public onClick ($event: Event) { 10 public onClick ($event: Event) {
11 const target = $event.target as HTMLLinkElement 11 const target = $event.target as HTMLLinkElement
12 12
@@ -21,10 +21,10 @@ export class TimestampRouteTransformerDirective {
21 const ngxLinkParams = new URLSearchParams(ngxLink.search) 21 const ngxLinkParams = new URLSearchParams(ngxLink.search)
22 if (ngxLinkParams.has('start') !== true) return 22 if (ngxLinkParams.has('start') !== true) return
23 23
24 const separators = ['h', 'm', 's'] 24 const separators = [ 'h', 'm', 's' ]
25 const start = ngxLinkParams 25 const start = ngxLinkParams
26 .get('start') 26 .get('start')
27 .match(new RegExp('(\\d{1,9}[' + separators.join('') + '])','g')) // match digits before any given separator 27 .match(new RegExp('(\\d{1,9}[' + separators.join('') + '])', 'g')) // match digits before any given separator
28 .map(t => { 28 .map(t => {
29 if (t.includes('h')) return parseInt(t, 10) * 3600 29 if (t.includes('h')) return parseInt(t, 10) * 3600
30 if (t.includes('m')) return parseInt(t, 10) * 60 30 if (t.includes('m')) return parseInt(t, 10) * 60
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts
index 85100b653..2007bdecb 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -195,10 +195,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
195 195
196 private loadRouteParams () { 196 private loadRouteParams () {
197 this.paramsSub = this.route.params.subscribe(routeParams => { 197 this.paramsSub = this.route.params.subscribe(routeParams => {
198 const videoId = routeParams[ 'videoId' ] 198 const videoId = routeParams['videoId']
199 if (videoId) return this.loadVideo(videoId) 199 if (videoId) return this.loadVideo(videoId)
200 200
201 const playlistId = routeParams[ 'playlistId' ] 201 const playlistId = routeParams['playlistId']
202 if (playlistId) return this.loadPlaylist(playlistId) 202 if (playlistId) return this.loadPlaylist(playlistId)
203 }) 203 })
204 } 204 }
@@ -206,7 +206,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
206 private loadRouteQuery () { 206 private loadRouteQuery () {
207 this.queryParamsSub = this.route.queryParams.subscribe(queryParams => { 207 this.queryParamsSub = this.route.queryParams.subscribe(queryParams => {
208 // Handle the ?playlistPosition 208 // Handle the ?playlistPosition
209 const positionParam = queryParams[ 'playlistPosition' ] ?? 1 209 const positionParam = queryParams['playlistPosition'] ?? 1
210 210
211 this.playlistPosition = positionParam === 'last' 211 this.playlistPosition = positionParam === 'last'
212 ? -1 // Handle the "last" index 212 ? -1 // Handle the "last" index
@@ -219,7 +219,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
219 219
220 this.videoWatchPlaylist.updatePlaylistIndex(this.playlistPosition) 220 this.videoWatchPlaylist.updatePlaylistIndex(this.playlistPosition)
221 221
222 const start = queryParams[ 'start' ] 222 const start = queryParams['start']
223 if (this.player && start) this.player.currentTime(parseInt(start, 10)) 223 if (this.player && start) this.player.currentTime(parseInt(start, 10))
224 }) 224 })
225 } 225 }
@@ -237,7 +237,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
237 'filter:api.video-watch.video.get.result' 237 'filter:api.video-watch.video.get.result'
238 ) 238 )
239 239
240 forkJoin([ videoObs, this.videoCaptionService.listCaptions(videoId)]) 240 forkJoin([ videoObs, this.videoCaptionService.listCaptions(videoId) ])
241 .subscribe({ 241 .subscribe({
242 next: ([ video, captionsResult ]) => { 242 next: ([ video, captionsResult ]) => {
243 const queryParams = this.route.snapshot.queryParams 243 const queryParams = this.route.snapshot.queryParams
@@ -292,6 +292,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
292 const originUrl = errorBody.originUrl + (window.location.search ?? '') 292 const originUrl = errorBody.originUrl + (window.location.search ?? '')
293 293
294 const res = await this.confirmService.confirm( 294 const res = await this.confirmService.confirm(
295 // eslint-disable-next-line max-len
295 $localize`This video is not available on this instance. Do you want to be redirected on the origin instance: <a href="${originUrl}">${originUrl}</a>?`, 296 $localize`This video is not available on this instance. Do you want to be redirected on the origin instance: <a href="${originUrl}">${originUrl}</a>?`,
296 $localize`Redirection` 297 $localize`Redirection`
297 ) 298 )
@@ -312,7 +313,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
312 if (!errorMessage) return 313 if (!errorMessage) return
313 314
314 // Display a message in the video player instead of a notification 315 // Display a message in the video player instead of a notification
315 if (errorMessage.indexOf('from xs param') !== -1) { 316 if (errorMessage.includes('from xs param')) {
316 this.flushPlayer() 317 this.flushPlayer()
317 this.remoteServerDown = true 318 this.remoteServerDown = true
318 319
@@ -466,7 +467,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
466 467
467 if (this.nextVideoUUID) { 468 if (this.nextVideoUUID) {
468 this.router.navigate([ '/w', this.nextVideoUUID ]) 469 this.router.navigate([ '/w', this.nextVideoUUID ])
469 return
470 } 470 }
471 } 471 }
472 472
@@ -483,14 +483,14 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
483 483
484 private isAutoPlayNext () { 484 private isAutoPlayNext () {
485 return ( 485 return (
486 (this.user && this.user.autoPlayNextVideo) || 486 (this.user?.autoPlayNextVideo) ||
487 this.anonymousUser.autoPlayNextVideo 487 this.anonymousUser.autoPlayNextVideo
488 ) 488 )
489 } 489 }
490 490
491 private isPlaylistAutoPlayNext () { 491 private isPlaylistAutoPlayNext () {
492 return ( 492 return (
493 (this.user && this.user.autoPlayNextVideoPlaylist) || 493 (this.user?.autoPlayNextVideoPlaylist) ||
494 this.anonymousUser.autoPlayNextVideoPlaylist 494 this.anonymousUser.autoPlayNextVideoPlaylist
495 ) 495 )
496 } 496 }
@@ -508,9 +508,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
508 } 508 }
509 509
510 private buildPlayerManagerOptions (params: { 510 private buildPlayerManagerOptions (params: {
511 video: VideoDetails, 511 video: VideoDetails
512 videoCaptions: VideoCaption[], 512 videoCaptions: VideoCaption[]
513 urlOptions: CustomizationOptions & { playerMode: PlayerMode }, 513 urlOptions: CustomizationOptions & { playerMode: PlayerMode }
514 user?: AuthUser 514 user?: AuthUser
515 }) { 515 }) {
516 const { video, videoCaptions, urlOptions, user } = params 516 const { video, videoCaptions, urlOptions, user } = params
@@ -573,10 +573,12 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
573 573
574 language: this.localeId, 574 language: this.localeId,
575 575
576 userWatching: user && user.videosHistoryEnabled === true ? { 576 userWatching: user && user.videosHistoryEnabled === true
577 url: this.videoService.getUserWatchingVideoUrl(video.uuid), 577 ? {
578 authorizationHeader: this.authService.getRequestHeaderValue() 578 url: this.videoService.getUserWatchingVideoUrl(video.uuid),
579 } : undefined, 579 authorizationHeader: this.authService.getRequestHeaderValue()
580 }
581 : undefined,
580 582
581 serverUrl: environment.apiUrl, 583 serverUrl: environment.apiUrl,
582 584
@@ -704,9 +706,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
704 if (this.isUserLoggedIn()) { 706 if (this.isUserLoggedIn()) {
705 this.hotkeys = this.hotkeys.concat([ 707 this.hotkeys = this.hotkeys.concat([
706 new Hotkey('shift+s', () => { 708 new Hotkey('shift+s', () => {
707 this.subscribeButton.isSubscribedToAll() 709 if (this.subscribeButton.isSubscribedToAll()) this.subscribeButton.unsubscribe()
708 ? this.subscribeButton.unsubscribe() 710 else this.subscribeButton.subscribe()
709 : this.subscribeButton.subscribe()
710 711
711 return false 712 return false
712 }, undefined, $localize`Subscribe to the account`) 713 }, undefined, $localize`Subscribe to the account`)
diff --git a/client/src/app/+videos/video-list/overview/overview.service.ts b/client/src/app/+videos/video-list/overview/overview.service.ts
index 3aa64ebc8..12d2aa1cb 100644
--- a/client/src/app/+videos/video-list/overview/overview.service.ts
+++ b/client/src/app/+videos/video-list/overview/overview.service.ts
@@ -43,7 +43,7 @@ export class OverviewService {
43 43
44 // Build videos objects 44 // Build videos objects
45 for (const key of Object.keys(serverVideosOverview)) { 45 for (const key of Object.keys(serverVideosOverview)) {
46 for (const object of serverVideosOverview[ key ]) { 46 for (const object of serverVideosOverview[key]) {
47 observables.push( 47 observables.push(
48 of(object.videos) 48 of(object.videos)
49 .pipe( 49 .pipe(
diff --git a/client/src/app/+videos/video-list/trending/video-trending-header.component.ts b/client/src/app/+videos/video-list/trending/video-trending-header.component.ts
index 6c2b32a4f..c94655c74 100644
--- a/client/src/app/+videos/video-list/trending/video-trending-header.component.ts
+++ b/client/src/app/+videos/video-list/trending/video-trending-header.component.ts
@@ -15,7 +15,7 @@ interface VideoTrendingHeaderItem {
15} 15}
16 16
17@Component({ 17@Component({
18 selector: 'video-trending-title-page', 18 selector: 'my-video-trending-title-page',
19 styleUrls: [ './video-trending-header.component.scss' ], 19 styleUrls: [ './video-trending-header.component.scss' ],
20 templateUrl: './video-trending-header.component.html' 20 templateUrl: './video-trending-header.component.html'
21}) 21})
diff --git a/client/src/app/+videos/video-list/trending/video-trending.component.ts b/client/src/app/+videos/video-list/trending/video-trending.component.ts
index ebec672f3..085f29a8b 100644
--- a/client/src/app/+videos/video-list/trending/video-trending.component.ts
+++ b/client/src/app/+videos/video-list/trending/video-trending.component.ts
@@ -63,7 +63,7 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
63 63
64 if (oldSort !== this.sort) this.reloadVideos() 64 if (oldSort !== this.sort) this.reloadVideos()
65 } 65 }
66 ) 66 )
67 } 67 }
68 68
69 ngOnDestroy () { 69 ngOnDestroy () {
@@ -97,12 +97,12 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
97 97
98 getInjector () { 98 getInjector () {
99 return Injector.create({ 99 return Injector.create({
100 providers: [{ 100 providers: [ {
101 provide: 'data', 101 provide: 'data',
102 useValue: { 102 useValue: {
103 model: this.defaultSort 103 model: this.defaultSort
104 } 104 }
105 }] 105 } ]
106 }) 106 })
107 } 107 }
108 108
diff --git a/client/src/app/+videos/video-list/video-local.component.ts b/client/src/app/+videos/video-list/video-local.component.ts
index 4be8cd6b5..b576883d1 100644
--- a/client/src/app/+videos/video-list/video-local.component.ts
+++ b/client/src/app/+videos/video-list/video-local.component.ts
@@ -5,7 +5,7 @@ import { HooksService } from '@app/core/plugins/hooks.service'
5import { immutableAssign } from '@app/helpers' 5import { immutableAssign } from '@app/helpers'
6import { VideoService } from '@app/shared/shared-main' 6import { VideoService } from '@app/shared/shared-main'
7import { AbstractVideoList } from '@app/shared/shared-video-miniature' 7import { AbstractVideoList } from '@app/shared/shared-video-miniature'
8import { UserRight, VideoFilter, VideoSortField } from '@shared/models' 8import { VideoFilter, VideoSortField } from '@shared/models'
9 9
10@Component({ 10@Component({
11 selector: 'my-videos-local', 11 selector: 'my-videos-local',
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts
index c1e296182..ed5cc53d9 100644
--- a/client/src/app/app.component.ts
+++ b/client/src/app/app.component.ts
@@ -243,7 +243,7 @@ export class AppComponent implements OnInit, AfterViewInit {
243 // Inject JS 243 // Inject JS
244 if (this.serverConfig.instance.customizations.javascript) { 244 if (this.serverConfig.instance.customizations.javascript) {
245 try { 245 try {
246 // tslint:disable:no-eval 246 /* eslint-disable no-eval */
247 eval(this.serverConfig.instance.customizations.javascript) 247 eval(this.serverConfig.instance.customizations.javascript)
248 } catch (err) { 248 } catch (err) {
249 console.error('Cannot eval custom JavaScript.', err) 249 console.error('Cannot eval custom JavaScript.', err)
@@ -294,7 +294,7 @@ export class AppComponent implements OnInit, AfterViewInit {
294 294
295 private initHotkeys () { 295 private initHotkeys () {
296 this.hotkeysService.add([ 296 this.hotkeysService.add([
297 new Hotkey(['/', 's'], (event: KeyboardEvent): boolean => { 297 new Hotkey([ '/', 's' ], (event: KeyboardEvent): boolean => {
298 document.getElementById('search-video').focus() 298 document.getElementById('search-video').focus()
299 return false 299 return false
300 }, undefined, $localize`Focus the search bar`), 300 }, undefined, $localize`Focus the search bar`),
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts
index 5da66c981..79239a17a 100644
--- a/client/src/app/core/auth/auth.service.ts
+++ b/client/src/app/core/auth/auth.service.ts
@@ -48,7 +48,7 @@ export class AuthService {
48 private hotkeysService: HotkeysService, 48 private hotkeysService: HotkeysService,
49 private restExtractor: RestExtractor, 49 private restExtractor: RestExtractor,
50 private router: Router 50 private router: Router
51 ) { 51 ) {
52 this.loginChanged = new Subject<AuthStatus>() 52 this.loginChanged = new Subject<AuthStatus>()
53 this.loginChangedSource = this.loginChanged.asObservable() 53 this.loginChangedSource = this.loginChanged.asObservable()
54 54
@@ -206,7 +206,9 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
206 this.refreshingTokenObservable = this.http.post<UserRefreshToken>(AuthService.BASE_TOKEN_URL, body, { headers }) 206 this.refreshingTokenObservable = this.http.post<UserRefreshToken>(AuthService.BASE_TOKEN_URL, body, { headers })
207 .pipe( 207 .pipe(
208 map(res => this.handleRefreshToken(res)), 208 map(res => this.handleRefreshToken(res)),
209 tap(() => { this.refreshingTokenObservable = null }), 209 tap(() => {
210 this.refreshingTokenObservable = null
211 }),
210 catchError(err => { 212 catchError(err => {
211 this.refreshingTokenObservable = null 213 this.refreshingTokenObservable = null
212 214
diff --git a/client/src/app/core/hotkeys/hotkeys.component.ts b/client/src/app/core/hotkeys/hotkeys.component.ts
index 315e1a25e..60b7516aa 100644
--- a/client/src/app/core/hotkeys/hotkeys.component.ts
+++ b/client/src/app/core/hotkeys/hotkeys.component.ts
@@ -3,8 +3,8 @@ import { Subscription } from 'rxjs'
3import { Component, Input, OnDestroy, OnInit } from '@angular/core' 3import { Component, Input, OnDestroy, OnInit } from '@angular/core'
4 4
5@Component({ 5@Component({
6 selector : 'my-hotkeys-cheatsheet', 6 selector: 'my-hotkeys-cheatsheet',
7 templateUrl : './hotkeys.component.html', 7 templateUrl: './hotkeys.component.html',
8 styleUrls: [ './hotkeys.component.scss' ] 8 styleUrls: [ './hotkeys.component.scss' ]
9}) 9})
10export class CheatSheetComponent implements OnInit, OnDestroy { 10export class CheatSheetComponent implements OnInit, OnDestroy {
@@ -16,7 +16,7 @@ export class CheatSheetComponent implements OnInit, OnDestroy {
16 16
17 constructor ( 17 constructor (
18 private hotkeysService: HotkeysService 18 private hotkeysService: HotkeysService
19 ) {} 19 ) {}
20 20
21 public ngOnInit (): void { 21 public ngOnInit (): void {
22 this.subscription = this.hotkeysService.cheatSheetToggle.subscribe((isOpen) => { 22 this.subscription = this.hotkeysService.cheatSheetToggle.subscribe((isOpen) => {
diff --git a/client/src/app/core/menu/menu.service.ts b/client/src/app/core/menu/menu.service.ts
index 0b8d0191e..f0dc4fcaa 100644
--- a/client/src/app/core/menu/menu.service.ts
+++ b/client/src/app/core/menu/menu.service.ts
@@ -25,7 +25,7 @@ export type MenuSection = {
25export class MenuService { 25export class MenuService {
26 isMenuDisplayed = true 26 isMenuDisplayed = true
27 isMenuChangedByUser = false 27 isMenuChangedByUser = false
28 menuWidth = 240 // should be kept equal to $menu-width 28 menuWidth = 240 // should be kept equal to $menu-width
29 29
30 constructor ( 30 constructor (
31 private screenService: ScreenService 31 private screenService: ScreenService
@@ -55,7 +55,7 @@ export class MenuService {
55 // On touch screens, lock body scroll and display content overlay when memu is opened 55 // On touch screens, lock body scroll and display content overlay when memu is opened
56 if (this.isMenuDisplayed) { 56 if (this.isMenuDisplayed) {
57 document.body.classList.add('menu-open') 57 document.body.classList.add('menu-open')
58 this.screenService.onFingerSwipe('left', () => { this.setMenuDisplay(false) }) 58 this.screenService.onFingerSwipe('left', () => this.setMenuDisplay(false))
59 return 59 return
60 } 60 }
61 61
diff --git a/client/src/app/core/plugins/hooks.service.ts b/client/src/app/core/plugins/hooks.service.ts
index ddde198d2..062083fd1 100644
--- a/client/src/app/core/plugins/hooks.service.ts
+++ b/client/src/app/core/plugins/hooks.service.ts
@@ -27,9 +27,8 @@ export class HooksService {
27 }) 27 })
28 } 28 }
29 29
30 wrapObsFun 30 wrapObsFun <P, R, H1 extends ClientFilterHookName, H2 extends ClientFilterHookName>
31 <P, R, H1 extends ClientFilterHookName, H2 extends ClientFilterHookName> 31 (fun: ObservableFunction<P, R>, params: P, scope: PluginClientScope, hookParamName: H1, hookResultName: H2) {
32 (fun: ObservableFunction<P, R>, params: P, scope: PluginClientScope, hookParamName: H1, hookResultName: H2) {
33 return from(this.pluginService.ensurePluginsAreLoaded(scope)) 32 return from(this.pluginService.ensurePluginsAreLoaded(scope))
34 .pipe( 33 .pipe(
35 mergeMap(() => this.wrapObjectWithoutScopeLoad(params, hookParamName)), 34 mergeMap(() => this.wrapObjectWithoutScopeLoad(params, hookParamName)),
@@ -38,9 +37,8 @@ export class HooksService {
38 ) 37 )
39 } 38 }
40 39
41 async wrapFun 40 async wrapFun <P, R, H1 extends ClientFilterHookName, H2 extends ClientFilterHookName>
42 <P, R, H1 extends ClientFilterHookName, H2 extends ClientFilterHookName> 41 (fun: RawFunction<P, R>, params: P, scope: PluginClientScope, hookParamName: H1, hookResultName: H2) {
43 (fun: RawFunction<P, R>, params: P, scope: PluginClientScope, hookParamName: H1, hookResultName: H2) {
44 await this.pluginService.ensurePluginsAreLoaded(scope) 42 await this.pluginService.ensurePluginsAreLoaded(scope)
45 43
46 const newParams = await this.wrapObjectWithoutScopeLoad(params, hookParamName) 44 const newParams = await this.wrapObjectWithoutScopeLoad(params, hookParamName)
diff --git a/client/src/app/core/plugins/plugin.service.ts b/client/src/app/core/plugins/plugin.service.ts
index 774c03964..89391c2c5 100644
--- a/client/src/app/core/plugins/plugin.service.ts
+++ b/client/src/app/core/plugins/plugin.service.ts
@@ -188,7 +188,7 @@ export class PluginService implements ClientHook {
188 if (!this.authService.isLoggedIn()) return undefined 188 if (!this.authService.isLoggedIn()) return undefined
189 189
190 const value = this.authService.getRequestHeaderValue() 190 const value = this.authService.getRequestHeaderValue()
191 return { 'Authorization': value } 191 return { Authorization: value }
192 }, 192 },
193 193
194 notifier: { 194 notifier: {
@@ -198,10 +198,10 @@ export class PluginService implements ClientHook {
198 }, 198 },
199 199
200 showModal: (input: { 200 showModal: (input: {
201 title: string, 201 title: string
202 content: string, 202 content: string
203 close?: boolean, 203 close?: boolean
204 cancel?: { value: string, action?: () => void }, 204 cancel?: { value: string, action?: () => void }
205 confirm?: { value: string, action?: () => void } 205 confirm?: { value: string, action?: () => void }
206 }) => { 206 }) => {
207 this.zone.run(() => this.customModal.show(input)) 207 this.zone.run(() => this.customModal.show(input))
diff --git a/client/src/app/core/renderer/markdown.service.ts b/client/src/app/core/renderer/markdown.service.ts
index 36258ca98..a81d99534 100644
--- a/client/src/app/core/renderer/markdown.service.ts
+++ b/client/src/app/core/renderer/markdown.service.ts
@@ -103,20 +103,20 @@ export class MarkdownService {
103 const { name, markdown, withEmoji, additionalAllowedTags } = options 103 const { name, markdown, withEmoji, additionalAllowedTags } = options
104 if (!markdown) return '' 104 if (!markdown) return ''
105 105
106 const config = this.parsersConfig[ name ] 106 const config = this.parsersConfig[name]
107 if (!this.markdownParsers[ name ]) { 107 if (!this.markdownParsers[name]) {
108 this.markdownParsers[ name ] = await this.createMarkdownIt(config) 108 this.markdownParsers[name] = await this.createMarkdownIt(config)
109 109
110 if (withEmoji) { 110 if (withEmoji) {
111 if (!this.emojiModule) { 111 if (!this.emojiModule) {
112 this.emojiModule = (await import('markdown-it-emoji/light')).default 112 this.emojiModule = (await import('markdown-it-emoji/light')).default
113 } 113 }
114 114
115 this.markdownParsers[ name ].use(this.emojiModule) 115 this.markdownParsers[name].use(this.emojiModule)
116 } 116 }
117 } 117 }
118 118
119 let html = this.markdownParsers[ name ].render(markdown) 119 let html = this.markdownParsers[name].render(markdown)
120 html = this.avoidTruncatedTags(html) 120 html = this.avoidTruncatedTags(html)
121 121
122 if (config.escape) return this.htmlRenderer.toSafeHtml(html, additionalAllowedTags) 122 if (config.escape) return this.htmlRenderer.toSafeHtml(html, additionalAllowedTags)
@@ -156,7 +156,7 @@ export class MarkdownService {
156 if (relIndex < 0) token.attrPush([ 'rel', 'noopener noreferrer' ]) 156 if (relIndex < 0) token.attrPush([ 'rel', 'noopener noreferrer' ])
157 else token.attrs[relIndex][1] = 'noopener noreferrer' 157 else token.attrs[relIndex][1] = 'noopener noreferrer'
158 158
159 // pass token to default renderer. 159 // pass token to default renderer.*
160 return defaultRender(tokens, index, options, env, self) 160 return defaultRender(tokens, index, options, env, self)
161 } 161 }
162 } 162 }
@@ -164,7 +164,7 @@ export class MarkdownService {
164 private avoidTruncatedTags (html: string) { 164 private avoidTruncatedTags (html: string) {
165 return html.replace(/\*\*?([^*]+)$/, '$1') 165 return html.replace(/\*\*?([^*]+)$/, '$1')
166 .replace(/<a[^>]+>([^<]+)<\/a>\s*...((<\/p>)|(<\/li>)|(<\/strong>))?$/mi, '$1...') 166 .replace(/<a[^>]+>([^<]+)<\/a>\s*...((<\/p>)|(<\/li>)|(<\/strong>))?$/mi, '$1...')
167 .replace(/\[[^\]]+\]\(([^\)]+)$/m, '$1') 167 .replace(/\[[^\]]+\]\(([^)]+)$/m, '$1')
168 .replace(/\s?\[[^\]]+\]?[.]{3}<\/p>$/m, '...</p>') 168 .replace(/\s?\[[^\]]+\]?[.]{3}<\/p>$/m, '...</p>')
169 } 169 }
170} 170}
diff --git a/client/src/app/core/rest/rest-extractor.service.ts b/client/src/app/core/rest/rest-extractor.service.ts
index 29a56ba39..dd4a78de5 100644
--- a/client/src/app/core/rest/rest-extractor.service.ts
+++ b/client/src/app/core/rest/rest-extractor.service.ts
@@ -13,15 +13,16 @@ export class RestExtractor {
13 return true 13 return true
14 } 14 }
15 15
16 applyToResultListData <T> (result: ResultList<T>, fun: Function, additionalArgs?: any[]): ResultList<T> { 16 applyToResultListData <T, A, U> (
17 result: ResultList<T>,
18 fun: (data: T, ...args: A[]) => U,
19 additionalArgs: A[] = []
20 ): ResultList<U> {
17 const data: T[] = result.data 21 const data: T[] = result.data
18 const newData: T[] = []
19
20 data.forEach(d => newData.push(fun.apply(this, [ d ].concat(additionalArgs))))
21 22
22 return { 23 return {
23 total: result.total, 24 total: result.total,
24 data: newData 25 data: data.map(d => fun.apply(this, [ d, ...additionalArgs ]))
25 } 26 }
26 } 27 }
27 28
@@ -29,8 +30,10 @@ export class RestExtractor {
29 return this.applyToResultListData(result, this.convertDateToHuman, [ fieldsToConvert ]) 30 return this.applyToResultListData(result, this.convertDateToHuman, [ fieldsToConvert ])
30 } 31 }
31 32
32 convertDateToHuman (target: { [ id: string ]: string }, fieldsToConvert: string[]) { 33 convertDateToHuman (target: any, fieldsToConvert: string[]) {
33 fieldsToConvert.forEach(field => target[field] = dateToHuman(target[field])) 34 fieldsToConvert.forEach(field => {
35 target[field] = dateToHuman(target[field])
36 })
34 37
35 return target 38 return target
36 } 39 }
@@ -46,7 +49,7 @@ export class RestExtractor {
46 errorMessage = err.error 49 errorMessage = err.error
47 } else if (err.status !== undefined) { 50 } else if (err.status !== undefined) {
48 // A server-side error occurred. 51 // A server-side error occurred.
49 if (err.error && err.error.errors) { 52 if (err.error?.errors) {
50 const errors = err.error.errors 53 const errors = err.error.errors
51 const errorsArray: string[] = [] 54 const errorsArray: string[] = []
52 55
@@ -55,9 +58,10 @@ export class RestExtractor {
55 }) 58 })
56 59
57 errorMessage = errorsArray.join('. ') 60 errorMessage = errorsArray.join('. ')
58 } else if (err.error && err.error.error) { 61 } else if (err.error?.error) {
59 errorMessage = err.error.error 62 errorMessage = err.error.error
60 } else if (err.status === HttpStatusCode.PAYLOAD_TOO_LARGE_413) { 63 } else if (err.status === HttpStatusCode.PAYLOAD_TOO_LARGE_413) {
64 // eslint-disable-next-line max-len
61 errorMessage = $localize`Media is too large for the server. Please contact you administrator if you want to increase the limit size.` 65 errorMessage = $localize`Media is too large for the server. Please contact you administrator if you want to increase the limit size.`
62 } else if (err.status === HttpStatusCode.TOO_MANY_REQUESTS_429) { 66 } else if (err.status === HttpStatusCode.TOO_MANY_REQUESTS_429) {
63 const secondsLeft = err.headers.get('retry-after') 67 const secondsLeft = err.headers.get('retry-after')
@@ -71,7 +75,7 @@ export class RestExtractor {
71 errorMessage = $localize`Server error. Please retry later.` 75 errorMessage = $localize`Server error. Please retry later.`
72 } 76 }
73 77
74 errorMessage = errorMessage ? errorMessage : 'Unknown error.' 78 errorMessage = errorMessage || 'Unknown error.'
75 console.error(`Backend returned code ${err.status}, errorMessage is: ${errorMessage}`) 79 console.error(`Backend returned code ${err.status}, errorMessage is: ${errorMessage}`)
76 } else { 80 } else {
77 console.error(err) 81 console.error(err)
@@ -93,7 +97,7 @@ export class RestExtractor {
93 } 97 }
94 98
95 redirectTo404IfNotFound (obj: { status: number }, type: 'video' | 'other', status = [ HttpStatusCode.NOT_FOUND_404 ]) { 99 redirectTo404IfNotFound (obj: { status: number }, type: 'video' | 'other', status = [ HttpStatusCode.NOT_FOUND_404 ]) {
96 if (obj && obj.status && status.indexOf(obj.status) !== -1) { 100 if (obj?.status && status.includes(obj.status)) {
97 // Do not use redirectService to avoid circular dependencies 101 // Do not use redirectService to avoid circular dependencies
98 this.router.navigate([ '/404' ], { state: { type, obj }, skipLocationChange: true }) 102 this.router.navigate([ '/404' ], { state: { type, obj }, skipLocationChange: true })
99 } 103 }
diff --git a/client/src/app/core/routing/custom-reuse-strategy.ts b/client/src/app/core/routing/custom-reuse-strategy.ts
index c0f9f04e0..c2510f1df 100644
--- a/client/src/app/core/routing/custom-reuse-strategy.ts
+++ b/client/src/app/core/routing/custom-reuse-strategy.ts
@@ -1,5 +1,5 @@
1import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router'
2import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router'
3 3
4@Injectable() 4@Injectable()
5export class CustomReuseStrategy implements RouteReuseStrategy { 5export class CustomReuseStrategy implements RouteReuseStrategy {
@@ -78,6 +78,6 @@ export class CustomReuseStrategy implements RouteReuseStrategy {
78 } 78 }
79 79
80 private isReuseEnabled (route: ActivatedRouteSnapshot) { 80 private isReuseEnabled (route: ActivatedRouteSnapshot) {
81 return route.data.reuse && route.data.reuse.enabled && route.queryParams[ 'a-state' ] 81 return route.data.reuse?.enabled && route.queryParams['a-state']
82 } 82 }
83} 83}
diff --git a/client/src/app/core/routing/menu-guard.service.ts b/client/src/app/core/routing/menu-guard.service.ts
index c4e64d434..8c5bbfde9 100644
--- a/client/src/app/core/routing/menu-guard.service.ts
+++ b/client/src/app/core/routing/menu-guard.service.ts
@@ -17,33 +17,43 @@ abstract class MenuGuard implements CanActivate, CanDeactivate<any> {
17 if (!this.screen.isInMobileView() && this.screen.isInMediumView()) { 17 if (!this.screen.isInMobileView() && this.screen.isInMediumView()) {
18 this.menu.setMenuDisplay(this.display) 18 this.menu.setMenuDisplay(this.display)
19 } 19 }
20
20 return true 21 return true
21 } 22 }
22} 23}
23 24
24@Injectable() 25@Injectable()
25export class OpenMenuGuard extends MenuGuard { 26export class OpenMenuGuard extends MenuGuard {
26 constructor (menu: MenuService, screen: ScreenService) { super(menu, screen, true) } 27 constructor (menu: MenuService, screen: ScreenService) {
28 super(menu, screen, true)
29 }
27} 30}
28 31
29@Injectable() 32@Injectable()
30export class OpenMenuAlwaysGuard extends MenuGuard { 33export class OpenMenuAlwaysGuard extends MenuGuard {
31 constructor (menu: MenuService, screen: ScreenService) { super(menu, screen, true) } 34 constructor (menu: MenuService, screen: ScreenService) {
35 super(menu, screen, true)
36 }
32 37
33 canActivate (): boolean { 38 canActivate (): boolean {
34 this.menu.setMenuDisplay(this.display) 39 this.menu.setMenuDisplay(this.display)
40
35 return true 41 return true
36 } 42 }
37} 43}
38 44
39@Injectable() 45@Injectable()
40export class CloseMenuGuard extends MenuGuard { 46export class CloseMenuGuard extends MenuGuard {
41 constructor (menu: MenuService, screen: ScreenService) { super(menu, screen, false) } 47 constructor (menu: MenuService, screen: ScreenService) {
48 super(menu, screen, false)
49 }
42} 50}
43 51
44@Injectable() 52@Injectable()
45export class CloseMenuAlwaysGuard extends MenuGuard { 53export class CloseMenuAlwaysGuard extends MenuGuard {
46 constructor (menu: MenuService, screen: ScreenService) { super(menu, screen, false) } 54 constructor (menu: MenuService, screen: ScreenService) {
55 super(menu, screen, false)
56 }
47 57
48 canActivate (): boolean { 58 canActivate (): boolean {
49 this.menu.setMenuDisplay(this.display) 59 this.menu.setMenuDisplay(this.display)
diff --git a/client/src/app/core/routing/meta-guard.service.ts b/client/src/app/core/routing/meta-guard.service.ts
index bedb3450e..851404959 100644
--- a/client/src/app/core/routing/meta-guard.service.ts
+++ b/client/src/app/core/routing/meta-guard.service.ts
@@ -1,5 +1,5 @@
1import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, RouterStateSnapshot } from '@angular/router' 2import { ActivatedRouteSnapshot, CanActivate, CanActivateChild } from '@angular/router'
3import { MetaService } from './meta.service' 3import { MetaService } from './meta.service'
4 4
5@Injectable() 5@Injectable()
diff --git a/client/src/app/core/routing/preload-selected-modules-list.ts b/client/src/app/core/routing/preload-selected-modules-list.ts
index b494a40bc..b5c3195b0 100644
--- a/client/src/app/core/routing/preload-selected-modules-list.ts
+++ b/client/src/app/core/routing/preload-selected-modules-list.ts
@@ -6,7 +6,7 @@ import { Injectable } from '@angular/core'
6@Injectable() 6@Injectable()
7export class PreloadSelectedModulesList implements PreloadingStrategy { 7export class PreloadSelectedModulesList implements PreloadingStrategy {
8 8
9 preload (route: Route, load: Function): Observable<any> { 9 preload (route: Route, load: () => Observable<any>): Observable<any> {
10 if (!route.data || !route.data.preload) return ofObservable(null) 10 if (!route.data || !route.data.preload) return ofObservable(null)
11 11
12 if (typeof route.data.preload === 'number') { 12 if (typeof route.data.preload === 'number') {
diff --git a/client/src/app/core/scoped-tokens/scoped-tokens.service.ts b/client/src/app/core/scoped-tokens/scoped-tokens.service.ts
index 8e3697c31..038e5031c 100644
--- a/client/src/app/core/scoped-tokens/scoped-tokens.service.ts
+++ b/client/src/app/core/scoped-tokens/scoped-tokens.service.ts
@@ -1,9 +1,8 @@
1import { Injectable } from '@angular/core' 1import { catchError } from 'rxjs/operators'
2import { HttpClient } from '@angular/common/http' 2import { HttpClient } from '@angular/common/http'
3import { environment } from '../../../environments/environment' 3import { Injectable } from '@angular/core'
4import { AuthService } from '../auth'
5import { ScopedToken } from '@shared/models/users/user-scoped-token' 4import { ScopedToken } from '@shared/models/users/user-scoped-token'
6import { catchError } from 'rxjs/operators' 5import { environment } from '../../../environments/environment'
7import { RestExtractor } from '../rest' 6import { RestExtractor } from '../rest'
8 7
9@Injectable() 8@Injectable()
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts
index 8f041a147..d01942139 100644
--- a/client/src/app/core/server/server.service.ts
+++ b/client/src/app/core/server/server.service.ts
@@ -4,7 +4,7 @@ import { HttpClient } from '@angular/common/http'
4import { Inject, Injectable, LOCALE_ID } from '@angular/core' 4import { Inject, Injectable, LOCALE_ID } from '@angular/core'
5import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers' 5import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
6import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n' 6import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
7import { HTMLServerConfig, SearchTargetType, ServerConfig, ServerStats, VideoConstant } from '@shared/models' 7import { HTMLServerConfig, ServerConfig, ServerStats, VideoConstant } from '@shared/models'
8import { environment } from '../../../environments/environment' 8import { environment } from '../../../environments/environment'
9 9
10@Injectable() 10@Injectable()
@@ -171,7 +171,7 @@ export class ServerService {
171 map(({ data, translations }) => { 171 map(({ data, translations }) => {
172 const hashToPopulate: VideoConstant<T>[] = Object.keys(data) 172 const hashToPopulate: VideoConstant<T>[] = Object.keys(data)
173 .map(dataKey => { 173 .map(dataKey => {
174 const label = data[ dataKey ] 174 const label = data[dataKey]
175 175
176 const id = attributeName === 'languages' 176 const id = attributeName === 'languages'
177 ? dataKey as T 177 ? dataKey as T
diff --git a/client/src/app/core/theme/theme.service.ts b/client/src/app/core/theme/theme.service.ts
index c35548798..c9e6fa700 100644
--- a/client/src/app/core/theme/theme.service.ts
+++ b/client/src/app/core/theme/theme.service.ts
@@ -95,8 +95,8 @@ export class ThemeService {
95 private loadTheme (name: string) { 95 private loadTheme (name: string) {
96 const links = document.getElementsByTagName('link') 96 const links = document.getElementsByTagName('link')
97 for (let i = 0; i < links.length; i++) { 97 for (let i = 0; i < links.length; i++) {
98 const link = links[ i ] 98 const link = links[i]
99 if (link.getAttribute('rel').indexOf('style') !== -1 && link.getAttribute('title')) { 99 if (link.getAttribute('rel').includes('style') && link.getAttribute('title')) {
100 link.disabled = link.getAttribute('title') !== name 100 link.disabled = link.getAttribute('title') !== name
101 } 101 }
102 } 102 }
diff --git a/client/src/app/core/users/user.service.ts b/client/src/app/core/users/user.service.ts
index 47db985e1..a8a774eca 100644
--- a/client/src/app/core/users/user.service.ts
+++ b/client/src/app/core/users/user.service.ts
@@ -35,7 +35,7 @@ export class UserService {
35 private restService: RestService, 35 private restService: RestService,
36 private localStorageService: LocalStorageService, 36 private localStorageService: LocalStorageService,
37 private sessionStorageService: SessionStorageService 37 private sessionStorageService: SessionStorageService
38 ) { } 38 ) { }
39 39
40 changePassword (currentPassword: string, newPassword: string) { 40 changePassword (currentPassword: string, newPassword: string) {
41 const url = UserService.BASE_USERS_URL + 'me' 41 const url = UserService.BASE_USERS_URL + 'me'
@@ -266,7 +266,7 @@ export class UserService {
266 266
267 getUserWithCache (userId: number) { 267 getUserWithCache (userId: number) {
268 if (!this.userCache[userId]) { 268 if (!this.userCache[userId]) {
269 this.userCache[ userId ] = this.getUser(userId).pipe(shareReplay()) 269 this.userCache[userId] = this.getUser(userId).pipe(shareReplay())
270 } 270 }
271 271
272 return this.userCache[userId] 272 return this.userCache[userId]
diff --git a/client/src/app/header/search-typeahead.component.ts b/client/src/app/header/search-typeahead.component.ts
index b5d76c70e..e10baea2b 100644
--- a/client/src/app/header/search-typeahead.component.ts
+++ b/client/src/app/header/search-typeahead.component.ts
@@ -199,7 +199,7 @@ export class SearchTypeaheadComponent implements OnInit, AfterViewChecked, OnDes
199 } 199 }
200 200
201 private loadUserLanguagesIfNeeded (queryParams: any) { 201 private loadUserLanguagesIfNeeded (queryParams: any) {
202 if (queryParams && queryParams.languageOneOf) return of(queryParams) 202 if (queryParams?.languageOneOf) return of(queryParams)
203 203
204 return this.authService.userInformationLoaded 204 return this.authService.userInformationLoaded
205 .pipe( 205 .pipe(
diff --git a/client/src/app/helpers/locales/oc.ts b/client/src/app/helpers/locales/oc.ts
index d3b2e8407..716b76df1 100644
--- a/client/src/app/helpers/locales/oc.ts
+++ b/client/src/app/helpers/locales/oc.ts
@@ -12,16 +12,16 @@ function plural (n: number): number {
12 12
13export default [ 13export default [
14 'oc', 14 'oc',
15 [['a. m.', 'p. m.'], u, u], 15 [ [ 'a. m.', 'p. m.' ], u, u ],
16 u, 16 u,
17 [ 17 [
18 ['dg', 'dl', 'dm', 'dc', 'dj', 'dv', 'ds'], ['dg.', 'dl.', 'dm.', 'dc.', 'dj.', 'dv.', 'ds.'], 18 [ 'dg', 'dl', 'dm', 'dc', 'dj', 'dv', 'ds' ], [ 'dg.', 'dl.', 'dm.', 'dc.', 'dj.', 'dv.', 'ds.' ],
19 ['dimenge', 'diluns', 'dimars', 'dimècres', 'dijòus', 'divendres', 'dissabte'], 19 [ 'dimenge', 'diluns', 'dimars', 'dimècres', 'dijòus', 'divendres', 'dissabte' ],
20 ['dg.', 'dl.', 'dm.', 'dc.', 'dj.', 'dv.', 'ds.'] 20 [ 'dg.', 'dl.', 'dm.', 'dc.', 'dj.', 'dv.', 'ds.' ]
21 ], 21 ],
22 u, 22 u,
23 [ 23 [
24 ['GN', 'FB', 'MÇ', 'AB', 'MA', 'JN', 'JL', 'AG', 'ST', 'OC', 'NV', 'DC'], 24 [ 'GN', 'FB', 'MÇ', 'AB', 'MA', 'JN', 'JL', 'AG', 'ST', 'OC', 'NV', 'DC' ],
25 [ 25 [
26 'de gen.', 'de febr.', 'de març', 'd’abr.', 'de mai', 'de junh', 'de jul.', 'd’ag.', 26 'de gen.', 'de febr.', 'de març', 'd’abr.', 'de mai', 'de junh', 'de jul.', 'd’ag.',
27 'de set.', 'd’oct.', 'de nov.', 'de dec.' 27 'de set.', 'd’oct.', 'de nov.', 'de dec.'
@@ -32,7 +32,7 @@ export default [
32 ] 32 ]
33 ], 33 ],
34 [ 34 [
35 ['GN', 'FB', 'MÇ', 'AB', 'MA', 'JN', 'JL', 'AG', 'ST', 'OC', 'NV', 'DC'], 35 [ 'GN', 'FB', 'MÇ', 'AB', 'MA', 'JN', 'JL', 'AG', 'ST', 'OC', 'NV', 'DC' ],
36 [ 36 [
37 'gen.', 'febr.', 'març', 'abr.', 'mai', 'junh', 'jul.', 'ag.', 'set.', 'oct.', 'nov.', 37 'gen.', 'febr.', 'març', 'abr.', 'mai', 'junh', 'jul.', 'ag.', 'set.', 'oct.', 'nov.',
38 'dec.' 38 'dec.'
@@ -42,62 +42,62 @@ export default [
42 'novembre', 'decembre' 42 'novembre', 'decembre'
43 ] 43 ]
44 ], 44 ],
45 [['aC', 'dC'], u, ['abans Jèsus-Crist', 'aprèp Jèsus-Crist']], 45 [ [ 'aC', 'dC' ], u, [ 'abans Jèsus-Crist', 'aprèp Jèsus-Crist' ] ],
46 1, 46 1,
47 [6, 0], 47 [ 6, 0 ],
48 ['d/M/yy', 'd MMM y', 'd MMMM \'de\' y', 'EEEE, d MMMM \'de\' y'], 48 [ 'd/M/yy', 'd MMM y', 'd MMMM \'de\' y', 'EEEE, d MMMM \'de\' y' ],
49 ['H:mm', 'H:mm:ss', 'H:mm:ss z', 'H:mm:ss zzzz'], 49 [ 'H:mm', 'H:mm:ss', 'H:mm:ss z', 'H:mm:ss zzzz' ],
50 ['{1} {0}', '{1}, {0}', '{1} \'a\' \'les\' {0}', u], 50 [ '{1} {0}', '{1}, {0}', '{1} \'a\' \'les\' {0}', u ],
51 [',', '.', ';', '%', '+', '-', 'E', '×', '‰', '∞', 'NaN', ':'], 51 [ ',', '.', ';', '%', '+', '-', 'E', '×', '‰', '∞', 'NaN', ':' ],
52 ['#,##0.###', '#,##0%', '#,##0.00 ¤', '#E0'], 52 [ '#,##0.###', '#,##0%', '#,##0.00 ¤', '#E0' ],
53 'EUR', 53 'EUR',
54 '€', 54 '€',
55 'euro', 55 'euro',
56 { 56 {
57 'ARS': ['$AR', '$'], 57 ARS: [ '$AR', '$' ],
58 'AUD': ['$AU', '$'], 58 AUD: [ '$AU', '$' ],
59 'BEF': ['FB'], 59 BEF: [ 'FB' ],
60 'BMD': ['$BM', '$'], 60 BMD: [ '$BM', '$' ],
61 'BND': ['$BN', '$'], 61 BND: [ '$BN', '$' ],
62 'BZD': ['$BZ', '$'], 62 BZD: [ '$BZ', '$' ],
63 'CAD': ['$CA', '$'], 63 CAD: [ '$CA', '$' ],
64 'CLP': ['$CL', '$'], 64 CLP: [ '$CL', '$' ],
65 'CNY': [u, 'Â¥'], 65 CNY: [ u, 'Â¥' ],
66 'COP': ['$CO', '$'], 66 COP: [ '$CO', '$' ],
67 'CYP': ['£CY'], 67 CYP: [ '£CY' ],
68 'EGP': [u, '£E'], 68 EGP: [ u, '£E' ],
69 'FJD': ['$FJ', '$'], 69 FJD: [ '$FJ', '$' ],
70 'FKP': ['£FK', '£'], 70 FKP: [ '£FK', '£' ],
71 'FRF': ['F'], 71 FRF: [ 'F' ],
72 'GBP': ['£GB', '£'], 72 GBP: [ '£GB', '£' ],
73 'GIP': ['£GI', '£'], 73 GIP: [ '£GI', '£' ],
74 'HKD': [u, '$'], 74 HKD: [ u, '$' ],
75 'IEP': ['£IE'], 75 IEP: [ '£IE' ],
76 'ILP': ['£IL'], 76 ILP: [ '£IL' ],
77 'ITL': ['₤IT'], 77 ITL: [ '₤IT' ],
78 'JPY': [u, 'Â¥'], 78 JPY: [ u, 'Â¥' ],
79 'KMF': [u, 'FC'], 79 KMF: [ u, 'FC' ],
80 'LBP': ['£LB', '£L'], 80 LBP: [ '£LB', '£L' ],
81 'MTP': ['£MT'], 81 MTP: [ '£MT' ],
82 'MXN': ['$MX', '$'], 82 MXN: [ '$MX', '$' ],
83 'NAD': ['$NA', '$'], 83 NAD: [ '$NA', '$' ],
84 'NIO': [u, '$C'], 84 NIO: [ u, '$C' ],
85 'NZD': ['$NZ', '$'], 85 NZD: [ '$NZ', '$' ],
86 'RHD': ['$RH'], 86 RHD: [ '$RH' ],
87 'RON': [u, 'L'], 87 RON: [ u, 'L' ],
88 'RWF': [u, 'FR'], 88 RWF: [ u, 'FR' ],
89 'SBD': ['$SB', '$'], 89 SBD: [ '$SB', '$' ],
90 'SGD': ['$SG', '$'], 90 SGD: [ '$SG', '$' ],
91 'SRD': ['$SR', '$'], 91 SRD: [ '$SR', '$' ],
92 'TOP': [u, '$T'], 92 TOP: [ u, '$T' ],
93 'TTD': ['$TT', '$'], 93 TTD: [ '$TT', '$' ],
94 'TWD': [u, 'NT$'], 94 TWD: [ u, 'NT$' ],
95 'USD': ['$US', '$'], 95 USD: [ '$US', '$' ],
96 'UYU': ['$UY', '$'], 96 UYU: [ '$UY', '$' ],
97 'WST': ['$WS'], 97 WST: [ '$WS' ],
98 'XCD': [u, '$'], 98 XCD: [ u, '$' ],
99 'XPF': ['FCFP'], 99 XPF: [ 'FCFP' ],
100 'ZMW': [u, 'Kw'] 100 ZMW: [ u, 'Kw' ]
101 }, 101 },
102 'ltr', 102 'ltr',
103 plural 103 plural
diff --git a/client/src/app/helpers/utils.ts b/client/src/app/helpers/utils.ts
index edcaf50e0..8636f3a55 100644
--- a/client/src/app/helpers/utils.ts
+++ b/client/src/app/helpers/utils.ts
@@ -10,7 +10,7 @@ import { AuthService } from '../core/auth'
10// Thanks: https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript 10// Thanks: https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
11function getParameterByName (name: string, url: string) { 11function getParameterByName (name: string, url: string) {
12 if (!url) url = window.location.href 12 if (!url) url = window.location.href
13 name = name.replace(/[\[\]]/g, '\\$&') 13 name = name.replace(/[[\]]/g, '\\$&')
14 14
15 const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)') 15 const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)')
16 const results = regex.exec(url) 16 const results = regex.exec(url)
@@ -110,10 +110,10 @@ function objectToFormData (obj: any, form?: FormData, namespace?: string) {
110 continue 110 continue
111 } 111 }
112 112
113 if (obj[key] !== null && typeof obj[ key ] === 'object' && !(obj[ key ] instanceof File)) { 113 if (obj[key] !== null && typeof obj[key] === 'object' && !(obj[key] instanceof File)) {
114 objectToFormData(obj[ key ], fd, formKey) 114 objectToFormData(obj[key], fd, formKey)
115 } else { 115 } else {
116 fd.append(formKey, obj[ key ]) 116 fd.append(formKey, obj[key])
117 } 117 }
118 } 118 }
119 119
@@ -159,7 +159,7 @@ function scrollToTop (behavior: 'auto' | 'smooth' = 'auto') {
159function isInViewport (el: HTMLElement) { 159function isInViewport (el: HTMLElement) {
160 const bounding = el.getBoundingClientRect() 160 const bounding = el.getBoundingClientRect()
161 return ( 161 return (
162 bounding.top >= 0 && 162 bounding.top >= 0 &&
163 bounding.left >= 0 && 163 bounding.left >= 0 &&
164 bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) && 164 bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
165 bounding.right <= (window.innerWidth || document.documentElement.clientWidth) 165 bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
diff --git a/client/src/app/menu/language-chooser.component.ts b/client/src/app/menu/language-chooser.component.ts
index e15aeff20..b42e41855 100644
--- a/client/src/app/menu/language-chooser.component.ts
+++ b/client/src/app/menu/language-chooser.component.ts
@@ -18,7 +18,7 @@ export class LanguageChooserComponent {
18 @Inject(LOCALE_ID) private localeId: string 18 @Inject(LOCALE_ID) private localeId: string
19 ) { 19 ) {
20 const l = Object.keys(I18N_LOCALES) 20 const l = Object.keys(I18N_LOCALES)
21 .map(k => ({ id: k, label: I18N_LOCALES[k] , iso: getShortLocale(k) })) 21 .map(k => ({ id: k, label: I18N_LOCALES[k], iso: getShortLocale(k) }))
22 22
23 this.languages = sortBy(l, 'label') 23 this.languages = sortBy(l, 'label')
24 } 24 }
diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts
index 627a8712f..97f07c956 100644
--- a/client/src/app/menu/menu.component.ts
+++ b/client/src/app/menu/menu.component.ts
@@ -28,7 +28,7 @@ const logger = debug('peertube:menu:MenuComponent')
28@Component({ 28@Component({
29 selector: 'my-menu', 29 selector: 'my-menu',
30 templateUrl: './menu.component.html', 30 templateUrl: './menu.component.html',
31 styleUrls: ['./menu.component.scss'] 31 styleUrls: [ './menu.component.scss' ]
32}) 32})
33export class MenuComponent implements OnInit { 33export class MenuComponent implements OnInit {
34 @ViewChild('languageChooserModal', { static: true }) languageChooserModal: LanguageChooserComponent 34 @ViewChild('languageChooserModal', { static: true }) languageChooserModal: LanguageChooserComponent
diff --git a/client/src/app/modal/custom-modal.component.ts b/client/src/app/modal/custom-modal.component.ts
index a98579085..559230e04 100644
--- a/client/src/app/modal/custom-modal.component.ts
+++ b/client/src/app/modal/custom-modal.component.ts
@@ -22,10 +22,10 @@ export class CustomModalComponent {
22 ) { } 22 ) { }
23 23
24 show (input: { 24 show (input: {
25 title: string, 25 title: string
26 content: string, 26 content: string
27 close?: boolean, 27 close?: boolean
28 cancel?: { value: string, action?: () => void }, 28 cancel?: { value: string, action?: () => void }
29 confirm?: { value: string, action?: () => void } 29 confirm?: { value: string, action?: () => void }
30 }) { 30 }) {
31 if (this.modalRef instanceof NgbModalRef && this.modalService.hasOpenModals()) { 31 if (this.modalRef instanceof NgbModalRef && this.modalService.hasOpenModals()) {
diff --git a/client/src/app/shared/form-validators/abuse-validators.ts b/client/src/app/shared/form-validators/abuse-validators.ts
index 75bfacf01..8d3c411b4 100644
--- a/client/src/app/shared/form-validators/abuse-validators.ts
+++ b/client/src/app/shared/form-validators/abuse-validators.ts
@@ -2,28 +2,28 @@ import { Validators } from '@angular/forms'
2import { BuildFormValidator } from './form-validator.model' 2import { BuildFormValidator } from './form-validator.model'
3 3
4export const ABUSE_REASON_VALIDATOR: BuildFormValidator = { 4export const ABUSE_REASON_VALIDATOR: BuildFormValidator = {
5 VALIDATORS: [Validators.required, Validators.minLength(2), Validators.maxLength(3000)], 5 VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ],
6 MESSAGES: { 6 MESSAGES: {
7 'required': $localize`Report reason is required.`, 7 required: $localize`Report reason is required.`,
8 'minlength': $localize`Report reason must be at least 2 characters long.`, 8 minlength: $localize`Report reason must be at least 2 characters long.`,
9 'maxlength': $localize`Report reason cannot be more than 3000 characters long.` 9 maxlength: $localize`Report reason cannot be more than 3000 characters long.`
10 } 10 }
11} 11}
12 12
13export const ABUSE_MODERATION_COMMENT_VALIDATOR: BuildFormValidator = { 13export const ABUSE_MODERATION_COMMENT_VALIDATOR: BuildFormValidator = {
14 VALIDATORS: [Validators.required, Validators.minLength(2), Validators.maxLength(3000)], 14 VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ],
15 MESSAGES: { 15 MESSAGES: {
16 'required': $localize`Moderation comment is required.`, 16 required: $localize`Moderation comment is required.`,
17 'minlength': $localize`Moderation comment must be at least 2 characters long.`, 17 minlength: $localize`Moderation comment must be at least 2 characters long.`,
18 'maxlength': $localize`Moderation comment cannot be more than 3000 characters long.` 18 maxlength: $localize`Moderation comment cannot be more than 3000 characters long.`
19 } 19 }
20} 20}
21 21
22export const ABUSE_MESSAGE_VALIDATOR: BuildFormValidator = { 22export const ABUSE_MESSAGE_VALIDATOR: BuildFormValidator = {
23 VALIDATORS: [Validators.required, Validators.minLength(2), Validators.maxLength(3000)], 23 VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ],
24 MESSAGES: { 24 MESSAGES: {
25 'required': $localize`Abuse message is required.`, 25 required: $localize`Abuse message is required.`,
26 'minlength': $localize`Abuse message must be at least 2 characters long.`, 26 minlength: $localize`Abuse message must be at least 2 characters long.`,
27 'maxlength': $localize`Abuse message cannot be more than 3000 characters long.` 27 maxlength: $localize`Abuse message cannot be more than 3000 characters long.`
28 } 28 }
29} 29}
diff --git a/client/src/app/shared/form-validators/custom-config-validators.ts b/client/src/app/shared/form-validators/custom-config-validators.ts
index 1ed5700ff..fbf423d08 100644
--- a/client/src/app/shared/form-validators/custom-config-validators.ts
+++ b/client/src/app/shared/form-validators/custom-config-validators.ts
@@ -2,120 +2,120 @@ import { Validators } from '@angular/forms'
2import { BuildFormValidator } from './form-validator.model' 2import { BuildFormValidator } from './form-validator.model'
3 3
4export const INSTANCE_NAME_VALIDATOR: BuildFormValidator = { 4export const INSTANCE_NAME_VALIDATOR: BuildFormValidator = {
5 VALIDATORS: [Validators.required], 5 VALIDATORS: [ Validators.required ],
6 MESSAGES: { 6 MESSAGES: {
7 'required': $localize`Instance name is required.` 7 required: $localize`Instance name is required.`
8 } 8 }
9} 9}
10 10
11export const INSTANCE_SHORT_DESCRIPTION_VALIDATOR: BuildFormValidator = { 11export const INSTANCE_SHORT_DESCRIPTION_VALIDATOR: BuildFormValidator = {
12 VALIDATORS: [Validators.max(250)], 12 VALIDATORS: [ Validators.max(250) ],
13 MESSAGES: { 13 MESSAGES: {
14 'max': $localize`Short description should not be longer than 250 characters.` 14 max: $localize`Short description should not be longer than 250 characters.`
15 } 15 }
16} 16}
17 17
18export const SERVICES_TWITTER_USERNAME_VALIDATOR: BuildFormValidator = { 18export const SERVICES_TWITTER_USERNAME_VALIDATOR: BuildFormValidator = {
19 VALIDATORS: [Validators.required], 19 VALIDATORS: [ Validators.required ],
20 MESSAGES: { 20 MESSAGES: {
21 'required': $localize`Twitter username is required.` 21 required: $localize`Twitter username is required.`
22 } 22 }
23} 23}
24 24
25export const CACHE_PREVIEWS_SIZE_VALIDATOR: BuildFormValidator = { 25export const CACHE_PREVIEWS_SIZE_VALIDATOR: BuildFormValidator = {
26 VALIDATORS: [Validators.required, Validators.min(1), Validators.pattern('[0-9]+')], 26 VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
27 MESSAGES: { 27 MESSAGES: {
28 'required': $localize`Previews cache size is required.`, 28 required: $localize`Previews cache size is required.`,
29 'min': $localize`Previews cache size must be greater than 1.`, 29 min: $localize`Previews cache size must be greater than 1.`,
30 'pattern': $localize`Previews cache size must be a number.` 30 pattern: $localize`Previews cache size must be a number.`
31 } 31 }
32} 32}
33 33
34export const CACHE_CAPTIONS_SIZE_VALIDATOR: BuildFormValidator = { 34export const CACHE_CAPTIONS_SIZE_VALIDATOR: BuildFormValidator = {
35 VALIDATORS: [Validators.required, Validators.min(1), Validators.pattern('[0-9]+')], 35 VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
36 MESSAGES: { 36 MESSAGES: {
37 'required': $localize`Captions cache size is required.`, 37 required: $localize`Captions cache size is required.`,
38 'min': $localize`Captions cache size must be greater than 1.`, 38 min: $localize`Captions cache size must be greater than 1.`,
39 'pattern': $localize`Captions cache size must be a number.` 39 pattern: $localize`Captions cache size must be a number.`
40 } 40 }
41} 41}
42 42
43export const SIGNUP_LIMIT_VALIDATOR: BuildFormValidator = { 43export const SIGNUP_LIMIT_VALIDATOR: BuildFormValidator = {
44 VALIDATORS: [Validators.required, Validators.min(-1), Validators.pattern('-?[0-9]+')], 44 VALIDATORS: [ Validators.required, Validators.min(-1), Validators.pattern('-?[0-9]+') ],
45 MESSAGES: { 45 MESSAGES: {
46 'required': $localize`Signup limit is required.`, 46 required: $localize`Signup limit is required.`,
47 'min': $localize`Signup limit must be greater than 1. Use -1 to disable it.`, 47 min: $localize`Signup limit must be greater than 1. Use -1 to disable it.`,
48 'pattern': $localize`Signup limit must be a number.` 48 pattern: $localize`Signup limit must be a number.`
49 } 49 }
50} 50}
51 51
52export const SIGNUP_MINIMUM_AGE_VALIDATOR: BuildFormValidator = { 52export const SIGNUP_MINIMUM_AGE_VALIDATOR: BuildFormValidator = {
53 VALIDATORS: [Validators.required, Validators.min(1), Validators.pattern('[0-9]+')], 53 VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
54 MESSAGES: { 54 MESSAGES: {
55 'required': $localize`Signup minimum age is required.`, 55 required: $localize`Signup minimum age is required.`,
56 'min': $localize`Signup minimum age must be greater than 1.`, 56 min: $localize`Signup minimum age must be greater than 1.`,
57 'pattern': $localize`Signup minimum age must be a number.` 57 pattern: $localize`Signup minimum age must be a number.`
58 } 58 }
59} 59}
60 60
61export const ADMIN_EMAIL_VALIDATOR: BuildFormValidator = { 61export const ADMIN_EMAIL_VALIDATOR: BuildFormValidator = {
62 VALIDATORS: [Validators.required, Validators.email], 62 VALIDATORS: [ Validators.required, Validators.email ],
63 MESSAGES: { 63 MESSAGES: {
64 'required': $localize`Admin email is required.`, 64 required: $localize`Admin email is required.`,
65 'email': $localize`Admin email must be valid.` 65 email: $localize`Admin email must be valid.`
66 } 66 }
67} 67}
68 68
69export const TRANSCODING_THREADS_VALIDATOR: BuildFormValidator = { 69export const TRANSCODING_THREADS_VALIDATOR: BuildFormValidator = {
70 VALIDATORS: [Validators.required, Validators.min(0)], 70 VALIDATORS: [ Validators.required, Validators.min(0) ],
71 MESSAGES: { 71 MESSAGES: {
72 'required': $localize`Transcoding threads is required.`, 72 required: $localize`Transcoding threads is required.`,
73 'min': $localize`Transcoding threads must be greater or equal to 0.` 73 min: $localize`Transcoding threads must be greater or equal to 0.`
74 } 74 }
75} 75}
76 76
77export const MAX_LIVE_DURATION_VALIDATOR: BuildFormValidator = { 77export const MAX_LIVE_DURATION_VALIDATOR: BuildFormValidator = {
78 VALIDATORS: [Validators.required, Validators.min(-1)], 78 VALIDATORS: [ Validators.required, Validators.min(-1) ],
79 MESSAGES: { 79 MESSAGES: {
80 'required': $localize`Max live duration is required.`, 80 required: $localize`Max live duration is required.`,
81 'min': $localize`Max live duration should be greater or equal to -1.` 81 min: $localize`Max live duration should be greater or equal to -1.`
82 } 82 }
83} 83}
84 84
85export const MAX_INSTANCE_LIVES_VALIDATOR: BuildFormValidator = { 85export const MAX_INSTANCE_LIVES_VALIDATOR: BuildFormValidator = {
86 VALIDATORS: [Validators.required, Validators.min(-1)], 86 VALIDATORS: [ Validators.required, Validators.min(-1) ],
87 MESSAGES: { 87 MESSAGES: {
88 'required': $localize`Max instance lives is required.`, 88 required: $localize`Max instance lives is required.`,
89 'min': $localize`Max instance lives should be greater or equal to -1.` 89 min: $localize`Max instance lives should be greater or equal to -1.`
90 } 90 }
91} 91}
92 92
93export const MAX_USER_LIVES_VALIDATOR: BuildFormValidator = { 93export const MAX_USER_LIVES_VALIDATOR: BuildFormValidator = {
94 VALIDATORS: [Validators.required, Validators.min(-1)], 94 VALIDATORS: [ Validators.required, Validators.min(-1) ],
95 MESSAGES: { 95 MESSAGES: {
96 'required': $localize`Max user lives is required.`, 96 required: $localize`Max user lives is required.`,
97 'min': $localize`Max user lives should be greater or equal to -1.` 97 min: $localize`Max user lives should be greater or equal to -1.`
98 } 98 }
99} 99}
100 100
101export const CONCURRENCY_VALIDATOR: BuildFormValidator = { 101export const CONCURRENCY_VALIDATOR: BuildFormValidator = {
102 VALIDATORS: [Validators.required, Validators.min(1)], 102 VALIDATORS: [ Validators.required, Validators.min(1) ],
103 MESSAGES: { 103 MESSAGES: {
104 'required': $localize`Concurrency is required.`, 104 required: $localize`Concurrency is required.`,
105 'min': $localize`Concurrency should be greater or equal to 1.` 105 min: $localize`Concurrency should be greater or equal to 1.`
106 } 106 }
107} 107}
108 108
109export const INDEX_URL_VALIDATOR: BuildFormValidator = { 109export const INDEX_URL_VALIDATOR: BuildFormValidator = {
110 VALIDATORS: [Validators.pattern(/^https:\/\//)], 110 VALIDATORS: [ Validators.pattern(/^https:\/\//) ],
111 MESSAGES: { 111 MESSAGES: {
112 'pattern': $localize`Index URL should be a URL` 112 pattern: $localize`Index URL should be a URL`
113 } 113 }
114} 114}
115 115
116export const SEARCH_INDEX_URL_VALIDATOR: BuildFormValidator = { 116export const SEARCH_INDEX_URL_VALIDATOR: BuildFormValidator = {
117 VALIDATORS: [Validators.pattern(/^https?:\/\//)], 117 VALIDATORS: [ Validators.pattern(/^https?:\/\//) ],
118 MESSAGES: { 118 MESSAGES: {
119 'pattern': $localize`Search index URL should be a URL` 119 pattern: $localize`Search index URL should be a URL`
120 } 120 }
121} 121}
diff --git a/client/src/app/shared/form-validators/form-validator.model.ts b/client/src/app/shared/form-validators/form-validator.model.ts
index 07b1ea075..6f2472ccd 100644
--- a/client/src/app/shared/form-validators/form-validator.model.ts
+++ b/client/src/app/shared/form-validators/form-validator.model.ts
@@ -1,7 +1,7 @@
1import { ValidatorFn } from '@angular/forms' 1import { ValidatorFn } from '@angular/forms'
2 2
3export type BuildFormValidator = { 3export type BuildFormValidator = {
4 VALIDATORS: ValidatorFn[], 4 VALIDATORS: ValidatorFn[]
5 MESSAGES: { [ name: string ]: string } 5 MESSAGES: { [ name: string ]: string }
6} 6}
7 7
diff --git a/client/src/app/shared/form-validators/host-validators.ts b/client/src/app/shared/form-validators/host-validators.ts
index 6f410a50a..3d9c476b5 100644
--- a/client/src/app/shared/form-validators/host-validators.ts
+++ b/client/src/app/shared/form-validators/host-validators.ts
@@ -4,7 +4,7 @@ import { BuildFormValidator } from './form-validator.model'
4export function validateHost (value: string) { 4export function validateHost (value: string) {
5 // Thanks to http://stackoverflow.com/a/106223 5 // Thanks to http://stackoverflow.com/a/106223
6 const HOST_REGEXP = new RegExp( 6 const HOST_REGEXP = new RegExp(
7 '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' 7 '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]).)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$'
8 ) 8 )
9 9
10 return HOST_REGEXP.test(value) 10 return HOST_REGEXP.test(value)
@@ -32,7 +32,7 @@ const validHosts: ValidatorFn = (control: AbstractControl) => {
32 if (errors.length === 0) return null 32 if (errors.length === 0) return null
33 33
34 return { 34 return {
35 'validHosts': { 35 validHosts: {
36 reason: 'invalid', 36 reason: 'invalid',
37 value: errors.join('. ') + '.' 37 value: errors.join('. ') + '.'
38 } 38 }
@@ -55,7 +55,7 @@ const validHostsOrHandles: ValidatorFn = (control: AbstractControl) => {
55 if (errors.length === 0) return null 55 if (errors.length === 0) return null
56 56
57 return { 57 return {
58 'validHostsOrHandles': { 58 validHostsOrHandles: {
59 reason: 'invalid', 59 reason: 'invalid',
60 value: errors.join('. ') + '.' 60 value: errors.join('. ') + '.'
61 } 61 }
@@ -80,7 +80,7 @@ export const unique: ValidatorFn = (control: AbstractControl) => {
80 } 80 }
81 81
82 return { 82 return {
83 'unique': { 83 unique: {
84 reason: 'invalid' 84 reason: 'invalid'
85 } 85 }
86 } 86 }
@@ -89,17 +89,17 @@ export const unique: ValidatorFn = (control: AbstractControl) => {
89export const UNIQUE_HOSTS_VALIDATOR: BuildFormValidator = { 89export const UNIQUE_HOSTS_VALIDATOR: BuildFormValidator = {
90 VALIDATORS: [ Validators.required, validHosts, unique ], 90 VALIDATORS: [ Validators.required, validHosts, unique ],
91 MESSAGES: { 91 MESSAGES: {
92 'required': $localize`Domain is required.`, 92 required: $localize`Domain is required.`,
93 'validHosts': $localize`Hosts entered are invalid.`, 93 validHosts: $localize`Hosts entered are invalid.`,
94 'unique': $localize`Hosts entered contain duplicates.` 94 unique: $localize`Hosts entered contain duplicates.`
95 } 95 }
96} 96}
97 97
98export const UNIQUE_HOSTS_OR_HANDLE_VALIDATOR: BuildFormValidator = { 98export const UNIQUE_HOSTS_OR_HANDLE_VALIDATOR: BuildFormValidator = {
99 VALIDATORS: [ Validators.required, validHostsOrHandles, unique ], 99 VALIDATORS: [ Validators.required, validHostsOrHandles, unique ],
100 MESSAGES: { 100 MESSAGES: {
101 'required': $localize`Domain is required.`, 101 required: $localize`Domain is required.`,
102 'validHostsOrHandles': $localize`Hosts or handles are invalid.`, 102 validHostsOrHandles: $localize`Hosts or handles are invalid.`,
103 'unique': $localize`Hosts or handles contain duplicates.` 103 unique: $localize`Hosts or handles contain duplicates.`
104 } 104 }
105} 105}
diff --git a/client/src/app/shared/form-validators/instance-validators.ts b/client/src/app/shared/form-validators/instance-validators.ts
index a72e28ba0..4b1f0d048 100644
--- a/client/src/app/shared/form-validators/instance-validators.ts
+++ b/client/src/app/shared/form-validators/instance-validators.ts
@@ -2,10 +2,10 @@ import { Validators } from '@angular/forms'
2import { BuildFormValidator } from './form-validator.model' 2import { BuildFormValidator } from './form-validator.model'
3 3
4export const FROM_EMAIL_VALIDATOR: BuildFormValidator = { 4export const FROM_EMAIL_VALIDATOR: BuildFormValidator = {
5 VALIDATORS: [Validators.required, Validators.email], 5 VALIDATORS: [ Validators.required, Validators.email ],
6 MESSAGES: { 6 MESSAGES: {
7 'required': $localize`Email is required.`, 7 required: $localize`Email is required.`,
8 'email': $localize`Email must be valid.` 8 email: $localize`Email must be valid.`
9 } 9 }
10} 10}
11 11
@@ -16,9 +16,9 @@ export const FROM_NAME_VALIDATOR: BuildFormValidator = {
16 Validators.maxLength(120) 16 Validators.maxLength(120)
17 ], 17 ],
18 MESSAGES: { 18 MESSAGES: {
19 'required': $localize`Your name is required.`, 19 required: $localize`Your name is required.`,
20 'minlength': $localize`Your name must be at least 1 character long.`, 20 minlength: $localize`Your name must be at least 1 character long.`,
21 'maxlength': $localize`Your name cannot be more than 120 characters long.` 21 maxlength: $localize`Your name cannot be more than 120 characters long.`
22 } 22 }
23} 23}
24 24
@@ -29,9 +29,9 @@ export const SUBJECT_VALIDATOR: BuildFormValidator = {
29 Validators.maxLength(120) 29 Validators.maxLength(120)
30 ], 30 ],
31 MESSAGES: { 31 MESSAGES: {
32 'required': $localize`A subject is required.`, 32 required: $localize`A subject is required.`,
33 'minlength': $localize`The subject must be at least 1 character long.`, 33 minlength: $localize`The subject must be at least 1 character long.`,
34 'maxlength': $localize`The subject cannot be more than 120 characters long.` 34 maxlength: $localize`The subject cannot be more than 120 characters long.`
35 } 35 }
36} 36}
37 37
@@ -42,8 +42,8 @@ export const BODY_VALIDATOR: BuildFormValidator = {
42 Validators.maxLength(5000) 42 Validators.maxLength(5000)
43 ], 43 ],
44 MESSAGES: { 44 MESSAGES: {
45 'required': $localize`A message is required.`, 45 required: $localize`A message is required.`,
46 'minlength': $localize`The message must be at least 3 characters long.`, 46 minlength: $localize`The message must be at least 3 characters long.`,
47 'maxlength': $localize`The message cannot be more than 5000 characters long.` 47 maxlength: $localize`The message cannot be more than 5000 characters long.`
48 } 48 }
49} 49}
diff --git a/client/src/app/shared/form-validators/login-validators.ts b/client/src/app/shared/form-validators/login-validators.ts
index 1ceae1be3..5b911ac47 100644
--- a/client/src/app/shared/form-validators/login-validators.ts
+++ b/client/src/app/shared/form-validators/login-validators.ts
@@ -6,7 +6,7 @@ export const LOGIN_USERNAME_VALIDATOR: BuildFormValidator = {
6 Validators.required 6 Validators.required
7 ], 7 ],
8 MESSAGES: { 8 MESSAGES: {
9 'required': $localize`Username is required.` 9 required: $localize`Username is required.`
10 } 10 }
11} 11}
12 12
@@ -15,6 +15,6 @@ export const LOGIN_PASSWORD_VALIDATOR: BuildFormValidator = {
15 Validators.required 15 Validators.required
16 ], 16 ],
17 MESSAGES: { 17 MESSAGES: {
18 'required': $localize`Password is required.` 18 required: $localize`Password is required.`
19 } 19 }
20} 20}
diff --git a/client/src/app/shared/form-validators/reset-password-validators.ts b/client/src/app/shared/form-validators/reset-password-validators.ts
index b87f2eab9..70617a562 100644
--- a/client/src/app/shared/form-validators/reset-password-validators.ts
+++ b/client/src/app/shared/form-validators/reset-password-validators.ts
@@ -6,6 +6,6 @@ export const RESET_PASSWORD_CONFIRM_VALIDATOR: BuildFormValidator = {
6 Validators.required 6 Validators.required
7 ], 7 ],
8 MESSAGES: { 8 MESSAGES: {
9 'required': $localize`Confirmation of the password is required.` 9 required: $localize`Confirmation of the password is required.`
10 } 10 }
11} 11}
diff --git a/client/src/app/shared/form-validators/user-validators.ts b/client/src/app/shared/form-validators/user-validators.ts
index 976c97b87..6d0dea64e 100644
--- a/client/src/app/shared/form-validators/user-validators.ts
+++ b/client/src/app/shared/form-validators/user-validators.ts
@@ -11,10 +11,10 @@ export const USER_USERNAME_VALIDATOR: BuildFormValidator = {
11 Validators.pattern(new RegExp(`^${USER_USERNAME_REGEX_CHARACTERS}*$`)) 11 Validators.pattern(new RegExp(`^${USER_USERNAME_REGEX_CHARACTERS}*$`))
12 ], 12 ],
13 MESSAGES: { 13 MESSAGES: {
14 'required': $localize`Username is required.`, 14 required: $localize`Username is required.`,
15 'minlength': $localize`Username must be at least 1 character long.`, 15 minlength: $localize`Username must be at least 1 character long.`,
16 'maxlength': $localize`Username cannot be more than 50 characters long.`, 16 maxlength: $localize`Username cannot be more than 50 characters long.`,
17 'pattern': $localize`Username should be lowercase alphanumeric; dots and underscores are allowed.` 17 pattern: $localize`Username should be lowercase alphanumeric; dots and underscores are allowed.`
18 } 18 }
19} 19}
20 20
@@ -26,18 +26,18 @@ export const USER_CHANNEL_NAME_VALIDATOR: BuildFormValidator = {
26 Validators.pattern(/^[a-z0-9][a-z0-9._]*$/) 26 Validators.pattern(/^[a-z0-9][a-z0-9._]*$/)
27 ], 27 ],
28 MESSAGES: { 28 MESSAGES: {
29 'required': $localize`Channel name is required.`, 29 required: $localize`Channel name is required.`,
30 'minlength': $localize`Channel name must be at least 1 character long.`, 30 minlength: $localize`Channel name must be at least 1 character long.`,
31 'maxlength': $localize`Channel name cannot be more than 50 characters long.`, 31 maxlength: $localize`Channel name cannot be more than 50 characters long.`,
32 'pattern': $localize`Channel name should be lowercase, and can contain only alphanumeric characters, dots and underscores.` 32 pattern: $localize`Channel name should be lowercase, and can contain only alphanumeric characters, dots and underscores.`
33 } 33 }
34} 34}
35 35
36export const USER_EMAIL_VALIDATOR: BuildFormValidator = { 36export const USER_EMAIL_VALIDATOR: BuildFormValidator = {
37 VALIDATORS: [ Validators.required, Validators.email ], 37 VALIDATORS: [ Validators.required, Validators.email ],
38 MESSAGES: { 38 MESSAGES: {
39 'required': $localize`Email is required.`, 39 required: $localize`Email is required.`,
40 'email': $localize`Email must be valid.` 40 email: $localize`Email must be valid.`
41 } 41 }
42} 42}
43 43
@@ -47,8 +47,8 @@ export const USER_HANDLE_VALIDATOR: BuildFormValidator = {
47 Validators.pattern(/@.+/) 47 Validators.pattern(/@.+/)
48 ], 48 ],
49 MESSAGES: { 49 MESSAGES: {
50 'required': $localize`Handle is required.`, 50 required: $localize`Handle is required.`,
51 'pattern': $localize`Handle must be valid (eg. chocobozzz@example.com).` 51 pattern: $localize`Handle must be valid (eg. chocobozzz@example.com).`
52 } 52 }
53} 53}
54 54
@@ -57,7 +57,7 @@ export const USER_EXISTING_PASSWORD_VALIDATOR: BuildFormValidator = {
57 Validators.required 57 Validators.required
58 ], 58 ],
59 MESSAGES: { 59 MESSAGES: {
60 'required': $localize`Password is required.` 60 required: $localize`Password is required.`
61 } 61 }
62} 62}
63 63
@@ -68,9 +68,9 @@ export const USER_PASSWORD_VALIDATOR: BuildFormValidator = {
68 Validators.maxLength(255) 68 Validators.maxLength(255)
69 ], 69 ],
70 MESSAGES: { 70 MESSAGES: {
71 'required': $localize`Password is required.`, 71 required: $localize`Password is required.`,
72 'minlength': $localize`Password must be at least 6 characters long.`, 72 minlength: $localize`Password must be at least 6 characters long.`,
73 'maxlength': $localize`Password cannot be more than 255 characters long.` 73 maxlength: $localize`Password cannot be more than 255 characters long.`
74 } 74 }
75} 75}
76 76
@@ -80,37 +80,37 @@ export const USER_PASSWORD_OPTIONAL_VALIDATOR: BuildFormValidator = {
80 Validators.maxLength(255) 80 Validators.maxLength(255)
81 ], 81 ],
82 MESSAGES: { 82 MESSAGES: {
83 'minlength': $localize`Password must be at least 6 characters long.`, 83 minlength: $localize`Password must be at least 6 characters long.`,
84 'maxlength': $localize`Password cannot be more than 255 characters long.` 84 maxlength: $localize`Password cannot be more than 255 characters long.`
85 } 85 }
86} 86}
87 87
88export const USER_CONFIRM_PASSWORD_VALIDATOR: BuildFormValidator = { 88export const USER_CONFIRM_PASSWORD_VALIDATOR: BuildFormValidator = {
89 VALIDATORS: [], 89 VALIDATORS: [],
90 MESSAGES: { 90 MESSAGES: {
91 'matchPassword': $localize`The new password and the confirmed password do not correspond.` 91 matchPassword: $localize`The new password and the confirmed password do not correspond.`
92 } 92 }
93} 93}
94 94
95export const USER_VIDEO_QUOTA_VALIDATOR: BuildFormValidator = { 95export const USER_VIDEO_QUOTA_VALIDATOR: BuildFormValidator = {
96 VALIDATORS: [ Validators.required, Validators.min(-1) ], 96 VALIDATORS: [ Validators.required, Validators.min(-1) ],
97 MESSAGES: { 97 MESSAGES: {
98 'required': $localize`Video quota is required.`, 98 required: $localize`Video quota is required.`,
99 'min': $localize`Quota must be greater than -1.` 99 min: $localize`Quota must be greater than -1.`
100 } 100 }
101} 101}
102export const USER_VIDEO_QUOTA_DAILY_VALIDATOR: BuildFormValidator = { 102export const USER_VIDEO_QUOTA_DAILY_VALIDATOR: BuildFormValidator = {
103 VALIDATORS: [ Validators.required, Validators.min(-1) ], 103 VALIDATORS: [ Validators.required, Validators.min(-1) ],
104 MESSAGES: { 104 MESSAGES: {
105 'required': $localize`Daily upload limit is required.`, 105 required: $localize`Daily upload limit is required.`,
106 'min': $localize`Daily upload limit must be greater than -1.` 106 min: $localize`Daily upload limit must be greater than -1.`
107 } 107 }
108} 108}
109 109
110export const USER_ROLE_VALIDATOR: BuildFormValidator = { 110export const USER_ROLE_VALIDATOR: BuildFormValidator = {
111 VALIDATORS: [ Validators.required ], 111 VALIDATORS: [ Validators.required ],
112 MESSAGES: { 112 MESSAGES: {
113 'required': $localize`User role is required.` 113 required: $localize`User role is required.`
114 } 114 }
115} 115}
116 116
@@ -122,15 +122,15 @@ export const USER_DESCRIPTION_VALIDATOR: BuildFormValidator = {
122 Validators.maxLength(1000) 122 Validators.maxLength(1000)
123 ], 123 ],
124 MESSAGES: { 124 MESSAGES: {
125 'minlength': $localize`Description must be at least 3 characters long.`, 125 minlength: $localize`Description must be at least 3 characters long.`,
126 'maxlength': $localize`Description cannot be more than 1000 characters long.` 126 maxlength: $localize`Description cannot be more than 1000 characters long.`
127 } 127 }
128} 128}
129 129
130export const USER_TERMS_VALIDATOR: BuildFormValidator = { 130export const USER_TERMS_VALIDATOR: BuildFormValidator = {
131 VALIDATORS: [ Validators.requiredTrue ], 131 VALIDATORS: [ Validators.requiredTrue ],
132 MESSAGES: { 132 MESSAGES: {
133 'required': $localize`You must agree with the instance terms in order to register on it.` 133 required: $localize`You must agree with the instance terms in order to register on it.`
134 } 134 }
135} 135}
136 136
@@ -140,8 +140,8 @@ export const USER_BAN_REASON_VALIDATOR: BuildFormValidator = {
140 Validators.maxLength(250) 140 Validators.maxLength(250)
141 ], 141 ],
142 MESSAGES: { 142 MESSAGES: {
143 'minlength': $localize`Ban reason must be at least 3 characters long.`, 143 minlength: $localize`Ban reason must be at least 3 characters long.`,
144 'maxlength': $localize`Ban reason cannot be more than 250 characters long.` 144 maxlength: $localize`Ban reason cannot be more than 250 characters long.`
145 } 145 }
146} 146}
147 147
@@ -152,9 +152,9 @@ function buildDisplayNameValidator (required: boolean) {
152 Validators.maxLength(120) 152 Validators.maxLength(120)
153 ], 153 ],
154 MESSAGES: { 154 MESSAGES: {
155 'required': $localize`Display name is required.`, 155 required: $localize`Display name is required.`,
156 'minlength': $localize`Display name must be at least 1 character long.`, 156 minlength: $localize`Display name must be at least 1 character long.`,
157 'maxlength': $localize`Display name cannot be more than 50 characters long.` 157 maxlength: $localize`Display name cannot be more than 50 characters long.`
158 } 158 }
159 } 159 }
160 160
diff --git a/client/src/app/shared/form-validators/video-block-validators.ts b/client/src/app/shared/form-validators/video-block-validators.ts
index d3974aefe..cd2791b76 100644
--- a/client/src/app/shared/form-validators/video-block-validators.ts
+++ b/client/src/app/shared/form-validators/video-block-validators.ts
@@ -4,7 +4,7 @@ import { BuildFormValidator } from './form-validator.model'
4export const VIDEO_BLOCK_REASON_VALIDATOR: BuildFormValidator = { 4export const VIDEO_BLOCK_REASON_VALIDATOR: BuildFormValidator = {
5 VALIDATORS: [ Validators.minLength(2), Validators.maxLength(300) ], 5 VALIDATORS: [ Validators.minLength(2), Validators.maxLength(300) ],
6 MESSAGES: { 6 MESSAGES: {
7 'minlength': $localize`Block reason must be at least 2 characters long.`, 7 minlength: $localize`Block reason must be at least 2 characters long.`,
8 'maxlength': $localize`Block reason cannot be more than 300 characters long.` 8 maxlength: $localize`Block reason cannot be more than 300 characters long.`
9 } 9 }
10} 10}
diff --git a/client/src/app/shared/form-validators/video-captions-validators.ts b/client/src/app/shared/form-validators/video-captions-validators.ts
index 9742d2925..a16216422 100644
--- a/client/src/app/shared/form-validators/video-captions-validators.ts
+++ b/client/src/app/shared/form-validators/video-captions-validators.ts
@@ -4,13 +4,13 @@ import { BuildFormValidator } from './form-validator.model'
4export const VIDEO_CAPTION_LANGUAGE_VALIDATOR: BuildFormValidator = { 4export const VIDEO_CAPTION_LANGUAGE_VALIDATOR: BuildFormValidator = {
5 VALIDATORS: [ Validators.required ], 5 VALIDATORS: [ Validators.required ],
6 MESSAGES: { 6 MESSAGES: {
7 'required': $localize`Video caption language is required.` 7 required: $localize`Video caption language is required.`
8 } 8 }
9} 9}
10 10
11export const VIDEO_CAPTION_FILE_VALIDATOR: BuildFormValidator = { 11export const VIDEO_CAPTION_FILE_VALIDATOR: BuildFormValidator = {
12 VALIDATORS: [ Validators.required ], 12 VALIDATORS: [ Validators.required ],
13 MESSAGES: { 13 MESSAGES: {
14 'required': $localize`Video caption file is required.` 14 required: $localize`Video caption file is required.`
15 } 15 }
16} 16}
diff --git a/client/src/app/shared/form-validators/video-channel-validators.ts b/client/src/app/shared/form-validators/video-channel-validators.ts
index ba502ed01..48f5b1a2c 100644
--- a/client/src/app/shared/form-validators/video-channel-validators.ts
+++ b/client/src/app/shared/form-validators/video-channel-validators.ts
@@ -7,10 +7,10 @@ export const VIDEO_CHANNEL_NAME_VALIDATOR: BuildFormValidator = {
7 VALIDATORS: USER_USERNAME_VALIDATOR.VALIDATORS, 7 VALIDATORS: USER_USERNAME_VALIDATOR.VALIDATORS,
8 8
9 MESSAGES: { 9 MESSAGES: {
10 'required': $localize`Name is required.`, 10 required: $localize`Name is required.`,
11 'minlength': $localize`Name must be at least 1 character long.`, 11 minlength: $localize`Name must be at least 1 character long.`,
12 'maxlength': $localize`Name cannot be more than 50 characters long.`, 12 maxlength: $localize`Name cannot be more than 50 characters long.`,
13 'pattern': $localize`Name should be lowercase alphanumeric; dots and underscores are allowed.` 13 pattern: $localize`Name should be lowercase alphanumeric; dots and underscores are allowed.`
14 } 14 }
15} 15}
16 16
@@ -21,9 +21,9 @@ export const VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR: BuildFormValidator = {
21 Validators.maxLength(50) 21 Validators.maxLength(50)
22 ], 22 ],
23 MESSAGES: { 23 MESSAGES: {
24 'required': $localize`Display name is required.`, 24 required: $localize`Display name is required.`,
25 'minlength': $localize`Display name must be at least 1 character long.`, 25 minlength: $localize`Display name must be at least 1 character long.`,
26 'maxlength': $localize`Display name cannot be more than 50 characters long.` 26 maxlength: $localize`Display name cannot be more than 50 characters long.`
27 } 27 }
28} 28}
29 29
@@ -33,8 +33,8 @@ export const VIDEO_CHANNEL_DESCRIPTION_VALIDATOR: BuildFormValidator = {
33 Validators.maxLength(1000) 33 Validators.maxLength(1000)
34 ], 34 ],
35 MESSAGES: { 35 MESSAGES: {
36 'minlength': $localize`Description must be at least 3 characters long.`, 36 minlength: $localize`Description must be at least 3 characters long.`,
37 'maxlength': $localize`Description cannot be more than 1000 characters long.` 37 maxlength: $localize`Description cannot be more than 1000 characters long.`
38 } 38 }
39} 39}
40 40
@@ -44,7 +44,7 @@ export const VIDEO_CHANNEL_SUPPORT_VALIDATOR: BuildFormValidator = {
44 Validators.maxLength(1000) 44 Validators.maxLength(1000)
45 ], 45 ],
46 MESSAGES: { 46 MESSAGES: {
47 'minlength': $localize`Support text must be at least 3 characters long.`, 47 minlength: $localize`Support text must be at least 3 characters long.`,
48 'maxlength': $localize`Support text cannot be more than 1000 characters long` 48 maxlength: $localize`Support text cannot be more than 1000 characters long`
49 } 49 }
50} 50}
diff --git a/client/src/app/shared/form-validators/video-comment-validators.ts b/client/src/app/shared/form-validators/video-comment-validators.ts
index c56564d34..9e8f95e7c 100644
--- a/client/src/app/shared/form-validators/video-comment-validators.ts
+++ b/client/src/app/shared/form-validators/video-comment-validators.ts
@@ -4,8 +4,8 @@ import { BuildFormValidator } from './form-validator.model'
4export const VIDEO_COMMENT_TEXT_VALIDATOR: BuildFormValidator = { 4export const VIDEO_COMMENT_TEXT_VALIDATOR: BuildFormValidator = {
5 VALIDATORS: [ Validators.required, Validators.minLength(1), Validators.maxLength(3000) ], 5 VALIDATORS: [ Validators.required, Validators.minLength(1), Validators.maxLength(3000) ],
6 MESSAGES: { 6 MESSAGES: {
7 'required': $localize`Comment is required.`, 7 required: $localize`Comment is required.`,
8 'minlength': $localize`Comment must be at least 2 characters long.`, 8 minlength: $localize`Comment must be at least 2 characters long.`,
9 'maxlength': $localize`Comment cannot be more than 3000 characters long.` 9 maxlength: $localize`Comment cannot be more than 3000 characters long.`
10 } 10 }
11} 11}
diff --git a/client/src/app/shared/form-validators/video-ownership-change-validators.ts b/client/src/app/shared/form-validators/video-ownership-change-validators.ts
index e1a2df8a6..3e7823c49 100644
--- a/client/src/app/shared/form-validators/video-ownership-change-validators.ts
+++ b/client/src/app/shared/form-validators/video-ownership-change-validators.ts
@@ -4,21 +4,21 @@ import { BuildFormValidator } from './form-validator.model'
4export const OWNERSHIP_CHANGE_CHANNEL_VALIDATOR: BuildFormValidator = { 4export const OWNERSHIP_CHANGE_CHANNEL_VALIDATOR: BuildFormValidator = {
5 VALIDATORS: [ Validators.required ], 5 VALIDATORS: [ Validators.required ],
6 MESSAGES: { 6 MESSAGES: {
7 'required': $localize`The channel is required.` 7 required: $localize`The channel is required.`
8 } 8 }
9} 9}
10 10
11export const OWNERSHIP_CHANGE_USERNAME_VALIDATOR: BuildFormValidator = { 11export const OWNERSHIP_CHANGE_USERNAME_VALIDATOR: BuildFormValidator = {
12 VALIDATORS: [ Validators.required, localAccountValidator ], 12 VALIDATORS: [ Validators.required, localAccountValidator ],
13 MESSAGES: { 13 MESSAGES: {
14 'required': $localize`The username is required.`, 14 required: $localize`The username is required.`,
15 'localAccountOnly': $localize`You can only transfer ownership to a local account` 15 localAccountOnly: $localize`You can only transfer ownership to a local account`
16 } 16 }
17} 17}
18 18
19function localAccountValidator (control: AbstractControl): ValidationErrors { 19function localAccountValidator (control: AbstractControl): ValidationErrors {
20 if (control.value.includes('@')) { 20 if (control.value.includes('@')) {
21 return { 'localAccountOnly': true } 21 return { localAccountOnly: true }
22 } 22 }
23 23
24 return null 24 return null
diff --git a/client/src/app/shared/form-validators/video-playlist-validators.ts b/client/src/app/shared/form-validators/video-playlist-validators.ts
index 7e3d29458..63af637a3 100644
--- a/client/src/app/shared/form-validators/video-playlist-validators.ts
+++ b/client/src/app/shared/form-validators/video-playlist-validators.ts
@@ -9,9 +9,9 @@ export const VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR: BuildFormValidator = {
9 Validators.maxLength(120) 9 Validators.maxLength(120)
10 ], 10 ],
11 MESSAGES: { 11 MESSAGES: {
12 'required': $localize`Display name is required.`, 12 required: $localize`Display name is required.`,
13 'minlength': $localize`Display name must be at least 1 character long.`, 13 minlength: $localize`Display name must be at least 1 character long.`,
14 'maxlength': $localize`Display name cannot be more than 120 characters long.` 14 maxlength: $localize`Display name cannot be more than 120 characters long.`
15 } 15 }
16} 16}
17 17
@@ -20,7 +20,7 @@ export const VIDEO_PLAYLIST_PRIVACY_VALIDATOR: BuildFormValidator = {
20 Validators.required 20 Validators.required
21 ], 21 ],
22 MESSAGES: { 22 MESSAGES: {
23 'required': $localize`Privacy is required.` 23 required: $localize`Privacy is required.`
24 } 24 }
25} 25}
26 26
@@ -30,21 +30,21 @@ export const VIDEO_PLAYLIST_DESCRIPTION_VALIDATOR: BuildFormValidator = {
30 Validators.maxLength(1000) 30 Validators.maxLength(1000)
31 ], 31 ],
32 MESSAGES: { 32 MESSAGES: {
33 'minlength': $localize`Description must be at least 3 characters long.`, 33 minlength: $localize`Description must be at least 3 characters long.`,
34 'maxlength': $localize`Description cannot be more than 1000 characters long.` 34 maxlength: $localize`Description cannot be more than 1000 characters long.`
35 } 35 }
36} 36}
37 37
38export const VIDEO_PLAYLIST_CHANNEL_ID_VALIDATOR: BuildFormValidator = { 38export const VIDEO_PLAYLIST_CHANNEL_ID_VALIDATOR: BuildFormValidator = {
39 VALIDATORS: [], 39 VALIDATORS: [],
40 MESSAGES: { 40 MESSAGES: {
41 'required': $localize`The channel is required when the playlist is public.` 41 required: $localize`The channel is required when the playlist is public.`
42 } 42 }
43} 43}
44 44
45export function setPlaylistChannelValidator (channelControl: AbstractControl, privacy: VideoPlaylistPrivacy) { 45export function setPlaylistChannelValidator (channelControl: AbstractControl, privacy: VideoPlaylistPrivacy) {
46 if (privacy.toString() === VideoPlaylistPrivacy.PUBLIC.toString()) { 46 if (privacy.toString() === VideoPlaylistPrivacy.PUBLIC.toString()) {
47 channelControl.setValidators([Validators.required]) 47 channelControl.setValidators([ Validators.required ])
48 } else { 48 } else {
49 channelControl.setValidators(null) 49 channelControl.setValidators(null)
50 } 50 }
diff --git a/client/src/app/shared/form-validators/video-validators.ts b/client/src/app/shared/form-validators/video-validators.ts
index 1382a7747..f96189aa2 100644
--- a/client/src/app/shared/form-validators/video-validators.ts
+++ b/client/src/app/shared/form-validators/video-validators.ts
@@ -12,17 +12,17 @@ export const trimValidator: ValidatorFn = (control: FormControl) => {
12export const VIDEO_NAME_VALIDATOR: BuildFormValidator = { 12export const VIDEO_NAME_VALIDATOR: BuildFormValidator = {
13 VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(120) ], 13 VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(120) ],
14 MESSAGES: { 14 MESSAGES: {
15 'required': $localize`Video name is required.`, 15 required: $localize`Video name is required.`,
16 'minlength': $localize`Video name must be at least 3 characters long.`, 16 minlength: $localize`Video name must be at least 3 characters long.`,
17 'maxlength': $localize`Video name cannot be more than 120 characters long.`, 17 maxlength: $localize`Video name cannot be more than 120 characters long.`,
18 'spaces': $localize`Video name has leading or trailing whitespace.` 18 spaces: $localize`Video name has leading or trailing whitespace.`
19 } 19 }
20} 20}
21 21
22export const VIDEO_PRIVACY_VALIDATOR: BuildFormValidator = { 22export const VIDEO_PRIVACY_VALIDATOR: BuildFormValidator = {
23 VALIDATORS: [ Validators.required ], 23 VALIDATORS: [ Validators.required ],
24 MESSAGES: { 24 MESSAGES: {
25 'required': $localize`Video privacy is required.` 25 required: $localize`Video privacy is required.`
26 } 26 }
27} 27}
28 28
@@ -49,46 +49,46 @@ export const VIDEO_IMAGE_VALIDATOR: BuildFormValidator = {
49export const VIDEO_CHANNEL_VALIDATOR: BuildFormValidator = { 49export const VIDEO_CHANNEL_VALIDATOR: BuildFormValidator = {
50 VALIDATORS: [ Validators.required ], 50 VALIDATORS: [ Validators.required ],
51 MESSAGES: { 51 MESSAGES: {
52 'required': $localize`Video channel is required.` 52 required: $localize`Video channel is required.`
53 } 53 }
54} 54}
55 55
56export const VIDEO_DESCRIPTION_VALIDATOR: BuildFormValidator = { 56export const VIDEO_DESCRIPTION_VALIDATOR: BuildFormValidator = {
57 VALIDATORS: [ Validators.minLength(3), Validators.maxLength(10000) ], 57 VALIDATORS: [ Validators.minLength(3), Validators.maxLength(10000) ],
58 MESSAGES: { 58 MESSAGES: {
59 'minlength': $localize`Video description must be at least 3 characters long.`, 59 minlength: $localize`Video description must be at least 3 characters long.`,
60 'maxlength': $localize`Video description cannot be more than 10000 characters long.` 60 maxlength: $localize`Video description cannot be more than 10000 characters long.`
61 } 61 }
62} 62}
63 63
64export const VIDEO_TAG_VALIDATOR: BuildFormValidator = { 64export const VIDEO_TAG_VALIDATOR: BuildFormValidator = {
65 VALIDATORS: [ Validators.minLength(2), Validators.maxLength(30) ], 65 VALIDATORS: [ Validators.minLength(2), Validators.maxLength(30) ],
66 MESSAGES: { 66 MESSAGES: {
67 'minlength': $localize`A tag should be more than 2 characters long.`, 67 minlength: $localize`A tag should be more than 2 characters long.`,
68 'maxlength': $localize`A tag should be less than 30 characters long.` 68 maxlength: $localize`A tag should be less than 30 characters long.`
69 } 69 }
70} 70}
71 71
72export const VIDEO_TAGS_ARRAY_VALIDATOR: BuildFormValidator = { 72export const VIDEO_TAGS_ARRAY_VALIDATOR: BuildFormValidator = {
73 VALIDATORS: [ Validators.maxLength(5), arrayTagLengthValidator() ], 73 VALIDATORS: [ Validators.maxLength(5), arrayTagLengthValidator() ],
74 MESSAGES: { 74 MESSAGES: {
75 'maxlength': $localize`A maximum of 5 tags can be used on a video.`, 75 maxlength: $localize`A maximum of 5 tags can be used on a video.`,
76 'arrayTagLength': $localize`A tag should be more than 1 and less than 30 characters long.` 76 arrayTagLength: $localize`A tag should be more than 1 and less than 30 characters long.`
77 } 77 }
78} 78}
79 79
80export const VIDEO_SUPPORT_VALIDATOR: BuildFormValidator = { 80export const VIDEO_SUPPORT_VALIDATOR: BuildFormValidator = {
81 VALIDATORS: [ Validators.minLength(3), Validators.maxLength(1000) ], 81 VALIDATORS: [ Validators.minLength(3), Validators.maxLength(1000) ],
82 MESSAGES: { 82 MESSAGES: {
83 'minlength': $localize`Video support must be at least 3 characters long.`, 83 minlength: $localize`Video support must be at least 3 characters long.`,
84 'maxlength': $localize`Video support cannot be more than 1000 characters long.` 84 maxlength: $localize`Video support cannot be more than 1000 characters long.`
85 } 85 }
86} 86}
87 87
88export const VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR: BuildFormValidator = { 88export const VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR: BuildFormValidator = {
89 VALIDATORS: [ ], 89 VALIDATORS: [ ],
90 MESSAGES: { 90 MESSAGES: {
91 'required': $localize`A date is required to schedule video update.` 91 required: $localize`A date is required to schedule video update.`
92 } 92 }
93} 93}
94 94
@@ -105,6 +105,6 @@ function arrayTagLengthValidator (min = 2, max = 30): ValidatorFn {
105 return null 105 return null
106 } 106 }
107 107
108 return { 'arrayTagLength': true } 108 return { arrayTagLength: true }
109 } 109 }
110} 110}
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
index e3bf9e1fb..33e9fd8de 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
@@ -39,23 +39,23 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
39 39
40 inputFilters: AdvancedInputFilter[] = [ 40 inputFilters: AdvancedInputFilter[] = [
41 { 41 {
42 queryParams: { 'search': 'state:pending' }, 42 queryParams: { search: 'state:pending' },
43 label: $localize`Unsolved reports` 43 label: $localize`Unsolved reports`
44 }, 44 },
45 { 45 {
46 queryParams: { 'search': 'state:accepted' }, 46 queryParams: { search: 'state:accepted' },
47 label: $localize`Accepted reports` 47 label: $localize`Accepted reports`
48 }, 48 },
49 { 49 {
50 queryParams: { 'search': 'state:rejected' }, 50 queryParams: { search: 'state:rejected' },
51 label: $localize`Refused reports` 51 label: $localize`Refused reports`
52 }, 52 },
53 { 53 {
54 queryParams: { 'search': 'videoIs:blacklisted' }, 54 queryParams: { search: 'videoIs:blacklisted' },
55 label: $localize`Reports with blocked videos` 55 label: $localize`Reports with blocked videos`
56 }, 56 },
57 { 57 {
58 queryParams: { 'search': 'videoIs:deleted' }, 58 queryParams: { search: 'videoIs:deleted' },
59 label: $localize`Reports with deleted videos` 59 label: $localize`Reports with deleted videos`
60 } 60 }
61 ] 61 ]
diff --git a/client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts b/client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts
index 06f1555ea..ccb0c5262 100644
--- a/client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts
+++ b/client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts
@@ -50,7 +50,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
50 } 50 }
51 51
52 async banUser () { 52 async banUser () {
53 const moderationComment: string = this.form.value[ 'moderationComment' ] 53 const moderationComment: string = this.form.value['moderationComment']
54 54
55 this.abuseService.updateAbuse(this.abuseToComment, { moderationComment }) 55 this.abuseService.updateAbuse(this.abuseToComment, { moderationComment })
56 .subscribe({ 56 .subscribe({
diff --git a/client/src/app/shared/shared-abuse-list/processed-abuse.model.ts b/client/src/app/shared/shared-abuse-list/processed-abuse.model.ts
index fce1a8db3..194d52a33 100644
--- a/client/src/app/shared/shared-abuse-list/processed-abuse.model.ts
+++ b/client/src/app/shared/shared-abuse-list/processed-abuse.model.ts
@@ -5,7 +5,7 @@ import { Account } from '@app/shared/shared-main'
5// Don't use an abuse model because we need external services to compute some properties 5// Don't use an abuse model because we need external services to compute some properties
6// And this model is only used in this component 6// And this model is only used in this component
7export type ProcessedAbuse = AdminAbuse & { 7export type ProcessedAbuse = AdminAbuse & {
8 moderationCommentHtml?: string, 8 moderationCommentHtml?: string
9 reasonHtml?: string 9 reasonHtml?: string
10 embedHtml?: SafeHtml 10 embedHtml?: SafeHtml
11 updatedAt?: Date 11 updatedAt?: Date
diff --git a/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts
index dc9b72ddb..2c0e45e20 100644
--- a/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts
+++ b/client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts
@@ -51,7 +51,7 @@ export class ActorAvatarEditComponent implements OnInit {
51 onAvatarChange (input: HTMLInputElement) { 51 onAvatarChange (input: HTMLInputElement) {
52 this.avatarfileInput = new ElementRef(input) 52 this.avatarfileInput = new ElementRef(input)
53 53
54 const avatarfile = this.avatarfileInput.nativeElement.files[ 0 ] 54 const avatarfile = this.avatarfileInput.nativeElement.files[0]
55 if (avatarfile.size > this.maxAvatarSize) { 55 if (avatarfile.size > this.maxAvatarSize) {
56 this.notifier.error('Error', $localize`This image is too large.`) 56 this.notifier.error('Error', $localize`This image is too large.`)
57 return 57 return
diff --git a/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts
index c3f10c055..cba2c5db3 100644
--- a/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts
+++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts
@@ -40,14 +40,14 @@ export class ActorBannerEditComponent implements OnInit {
40 this.maxBannerSize = config.banner.file.size.max 40 this.maxBannerSize = config.banner.file.size.max
41 this.bannerExtensions = config.banner.file.extensions.join(', ') 41 this.bannerExtensions = config.banner.file.extensions.join(', ')
42 42
43 // tslint:disable:max-line-length 43 /* eslint-disable max-len */
44 this.bannerFormat = $localize`ratio 6/1, recommended size: 1920x317, max size: ${getBytes(this.maxBannerSize)}, extensions: ${this.bannerExtensions}` 44 this.bannerFormat = $localize`ratio 6/1, recommended size: 1920x317, max size: ${getBytes(this.maxBannerSize)}, extensions: ${this.bannerExtensions}`
45 } 45 }
46 46
47 onBannerChange (input: HTMLInputElement) { 47 onBannerChange (input: HTMLInputElement) {
48 this.bannerfileInput = new ElementRef(input) 48 this.bannerfileInput = new ElementRef(input)
49 49
50 const bannerfile = this.bannerfileInput.nativeElement.files[ 0 ] 50 const bannerfile = this.bannerfileInput.nativeElement.files[0]
51 if (bannerfile.size > this.maxBannerSize) { 51 if (bannerfile.size > this.maxBannerSize) {
52 this.notifier.error('Error', $localize`This image is too large.`) 52 this.notifier.error('Error', $localize`This image is too large.`)
53 return 53 return
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.ts b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
index b06c2bae6..a4adfd1b7 100644
--- a/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
+++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts
@@ -17,6 +17,8 @@ export type ActorAvatarSize = '18' | '25' | '32' | '34' | '36' | '40' | '100' |
17 templateUrl: './actor-avatar.component.html' 17 templateUrl: './actor-avatar.component.html'
18}) 18})
19export class ActorAvatarComponent { 19export class ActorAvatarComponent {
20 private _title: string
21
20 @Input() account: ActorInput 22 @Input() account: ActorInput
21 @Input() channel: ActorInput 23 @Input() channel: ActorInput
22 24
@@ -33,8 +35,6 @@ export class ActorAvatarComponent {
33 this._title = value 35 this._title = value
34 } 36 }
35 37
36 private _title: string
37
38 get title () { 38 get title () {
39 if (this._title) return this._title 39 if (this._title) return this._title
40 if (this.account) return $localize`${this.account.name} (account page)` 40 if (this.account) return $localize`${this.account.name} (account page)`
@@ -50,22 +50,6 @@ export class ActorAvatarComponent {
50 return '' 50 return ''
51 } 51 }
52 52
53 getClass (type: 'avatar' | 'initial') {
54 const base = [ 'avatar' ]
55
56 if (this.size) base.push(`avatar-${this.size}`)
57
58 if (this.channel) base.push('channel')
59 else base.push('account')
60
61 if (type === 'initial' && this.initial) {
62 base.push('initial')
63 base.push(this.getColorTheme())
64 }
65
66 return base
67 }
68
69 get defaultAvatarUrl () { 53 get defaultAvatarUrl () {
70 if (this.channel) return VideoChannel.GET_DEFAULT_AVATAR_URL() 54 if (this.channel) return VideoChannel.GET_DEFAULT_AVATAR_URL()
71 55
@@ -86,6 +70,22 @@ export class ActorAvatarComponent {
86 return name.slice(0, 1) 70 return name.slice(0, 1)
87 } 71 }
88 72
73 getClass (type: 'avatar' | 'initial') {
74 const base = [ 'avatar' ]
75
76 if (this.size) base.push(`avatar-${this.size}`)
77
78 if (this.channel) base.push('channel')
79 else base.push('account')
80
81 if (type === 'initial' && this.initial) {
82 base.push('initial')
83 base.push(this.getColorTheme())
84 }
85
86 return base
87 }
88
89 hasActor () { 89 hasActor () {
90 return !!this.account || !!this.channel 90 return !!this.account || !!this.channel
91 } 91 }
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts
index 8c1357d7a..35b413b60 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts
@@ -39,9 +39,13 @@ export class ChannelMiniatureMarkupComponent implements CustomMarkupComponent, O
39 ngOnInit () { 39 ngOnInit () {
40 this.findInBulk.getChannel(this.name) 40 this.findInBulk.getChannel(this.name)
41 .pipe( 41 .pipe(
42 tap(channel => this.channel = channel), 42 tap(channel => {
43 this.channel = channel
44 }),
43 switchMap(() => from(this.markdown.textMarkdownToHTML(this.channel.description))), 45 switchMap(() => from(this.markdown.textMarkdownToHTML(this.channel.description))),
44 tap(html => this.descriptionHTML = html), 46 tap(html => {
47 this.descriptionHTML = html
48 }),
45 switchMap(() => this.loadVideosObservable()), 49 switchMap(() => this.loadVideosObservable()),
46 finalize(() => this.loaded.emit(true)) 50 finalize(() => this.loaded.emit(true))
47 ).subscribe({ 51 ).subscribe({
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts
index 56b43d85e..7315126e0 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts
@@ -1,10 +1,10 @@
1import { finalize } from 'rxjs/operators' 1import { finalize } from 'rxjs/operators'
2import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' 2import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
3import { AuthService, Notifier } from '@app/core' 3import { AuthService, Notifier } from '@app/core'
4import { Video, VideoService } from '../../shared-main' 4import { FindInBulkService } from '@app/shared/shared-search'
5import { Video } from '../../shared-main'
5import { MiniatureDisplayOptions } from '../../shared-video-miniature' 6import { MiniatureDisplayOptions } from '../../shared-video-miniature'
6import { CustomMarkupComponent } from './shared' 7import { CustomMarkupComponent } from './shared'
7import { FindInBulkService } from '@app/shared/shared-search'
8 8
9/* 9/*
10 * Markup component that creates a video miniature only 10 * Markup component that creates a video miniature only
diff --git a/client/src/app/shared/shared-forms/form-reactive.ts b/client/src/app/shared/shared-forms/form-reactive.ts
index adf6cb894..f2ce82360 100644
--- a/client/src/app/shared/shared-forms/form-reactive.ts
+++ b/client/src/app/shared/shared-forms/form-reactive.ts
@@ -51,7 +51,7 @@ export abstract class FormReactive {
51 } 51 }
52 52
53 // clear previous error message (if any) 53 // clear previous error message (if any)
54 formErrors[ field ] = '' 54 formErrors[field] = ''
55 const control = form.get(field) 55 const control = form.get(field)
56 56
57 if (control.dirty) this.formChanged = true 57 if (control.dirty) this.formChanged = true
@@ -59,9 +59,9 @@ export abstract class FormReactive {
59 // Don't care if dirty on force check 59 // Don't care if dirty on force check
60 const isDirty = control.dirty || forceCheck === true 60 const isDirty = control.dirty || forceCheck === true
61 if (control && isDirty && control.enabled && !control.valid) { 61 if (control && isDirty && control.enabled && !control.valid) {
62 const messages = validationMessages[ field ] 62 const messages = validationMessages[field]
63 for (const key of Object.keys(control.errors)) { 63 for (const key of Object.keys(control.errors)) {
64 formErrors[ field ] += messages[ key ] + ' ' 64 formErrors[field] += messages[key] + ' '
65 } 65 }
66 } 66 }
67 } 67 }
diff --git a/client/src/app/shared/shared-forms/form-validator.service.ts b/client/src/app/shared/shared-forms/form-validator.service.ts
index 41c8b76bd..c0664de5f 100644
--- a/client/src/app/shared/shared-forms/form-validator.service.ts
+++ b/client/src/app/shared/shared-forms/form-validator.service.ts
@@ -28,11 +28,11 @@ export class FormValidatorService {
28 continue 28 continue
29 } 29 }
30 30
31 if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string } 31 if (field?.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string }
32 32
33 const defaultValue = defaultValues[name] || '' 33 const defaultValue = defaultValues[name] || ''
34 34
35 if (field && field.VALIDATORS) group[name] = [ defaultValue, field.VALIDATORS ] 35 if (field?.VALIDATORS) group[name] = [ defaultValue, field.VALIDATORS ]
36 else group[name] = [ defaultValue ] 36 else group[name] = [ defaultValue ]
37 } 37 }
38 38
@@ -62,11 +62,11 @@ export class FormValidatorService {
62 continue 62 continue
63 } 63 }
64 64
65 if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string } 65 if (field?.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string }
66 66
67 const defaultValue = defaultValues[name] || '' 67 const defaultValue = defaultValues[name] || ''
68 68
69 if (field && field.VALIDATORS) form.addControl(name, new FormControl(defaultValue, field.VALIDATORS as ValidatorFn[])) 69 if (field?.VALIDATORS) form.addControl(name, new FormControl(defaultValue, field.VALIDATORS as ValidatorFn[]))
70 else form.addControl(name, new FormControl(defaultValue)) 70 else form.addControl(name, new FormControl(defaultValue))
71 } 71 }
72 } 72 }
diff --git a/client/src/app/shared/shared-forms/reactive-file.component.ts b/client/src/app/shared/shared-forms/reactive-file.component.ts
index fac3dfb3a..9d27ad07a 100644
--- a/client/src/app/shared/shared-forms/reactive-file.component.ts
+++ b/client/src/app/shared/shared-forms/reactive-file.component.ts
@@ -43,7 +43,7 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor {
43 } 43 }
44 44
45 fileChange (event: any) { 45 fileChange (event: any) {
46 if (event.target.files && event.target.files.length) { 46 if (event.target.files?.length) {
47 const [ file ] = event.target.files 47 const [ file ] = event.target.files
48 48
49 if (file.size > this.maxFileSize) { 49 if (file.size > this.maxFileSize) {
diff --git a/client/src/app/shared/shared-icons/global-icon.component.ts b/client/src/app/shared/shared-icons/global-icon.component.ts
index a47f07fc3..cb5f31c8e 100644
--- a/client/src/app/shared/shared-icons/global-icon.component.ts
+++ b/client/src/app/shared/shared-icons/global-icon.component.ts
@@ -3,77 +3,77 @@ import { HooksService } from '@app/core/plugins/hooks.service'
3 3
4const icons = { 4const icons = {
5 // misc icons 5 // misc icons
6 'npm': require('!!raw-loader?!../../../assets/images/misc/npm.svg').default, 6 npm: require('!!raw-loader?!../../../assets/images/misc/npm.svg').default,
7 'markdown': require('!!raw-loader?!../../../assets/images/misc/markdown.svg').default, 7 markdown: require('!!raw-loader?!../../../assets/images/misc/markdown.svg').default,
8 'language': require('!!raw-loader?!../../../assets/images/misc/language.svg').default, 8 language: require('!!raw-loader?!../../../assets/images/misc/language.svg').default,
9 'video-lang': require('!!raw-loader?!../../../assets/images/misc/video-lang.svg').default, 9 'video-lang': require('!!raw-loader?!../../../assets/images/misc/video-lang.svg').default,
10 'support': require('!!raw-loader?!../../../assets/images/misc/support.svg').default, 10 support: require('!!raw-loader?!../../../assets/images/misc/support.svg').default,
11 'peertube-x': require('!!raw-loader?!../../../assets/images/misc/peertube-x.svg').default, 11 'peertube-x': require('!!raw-loader?!../../../assets/images/misc/peertube-x.svg').default,
12 'robot': require('!!raw-loader?!../../../assets/images/misc/miscellaneous-services.svg').default, // material ui 12 robot: require('!!raw-loader?!../../../assets/images/misc/miscellaneous-services.svg').default, // material ui
13 'videos': require('!!raw-loader?!../../../assets/images/misc/video-library.svg').default, // material ui 13 videos: require('!!raw-loader?!../../../assets/images/misc/video-library.svg').default, // material ui
14 'history': require('!!raw-loader?!../../../assets/images/misc/history.svg').default, // material ui 14 history: require('!!raw-loader?!../../../assets/images/misc/history.svg').default, // material ui
15 'subscriptions': require('!!raw-loader?!../../../assets/images/misc/subscriptions.svg').default, // material ui 15 subscriptions: require('!!raw-loader?!../../../assets/images/misc/subscriptions.svg').default, // material ui
16 'playlist-add': require('!!raw-loader?!../../../assets/images/misc/playlist-add.svg').default, // material ui 16 'playlist-add': require('!!raw-loader?!../../../assets/images/misc/playlist-add.svg').default, // material ui
17 'follower': require('!!raw-loader?!../../../assets/images/misc/account-arrow-left.svg').default, // material ui 17 follower: require('!!raw-loader?!../../../assets/images/misc/account-arrow-left.svg').default, // material ui
18 'following': require('!!raw-loader?!../../../assets/images/misc/account-arrow-right.svg').default, // material ui 18 following: require('!!raw-loader?!../../../assets/images/misc/account-arrow-right.svg').default, // material ui
19 'flame': require('!!raw-loader?!../../../assets/images/misc/flame.svg').default, 19 flame: require('!!raw-loader?!../../../assets/images/misc/flame.svg').default,
20 'local': require('!!raw-loader?!../../../assets/images/misc/local.svg').default, 20 local: require('!!raw-loader?!../../../assets/images/misc/local.svg').default,
21 21
22 // feather icons 22 // feather icons
23 'flag': require('!!raw-loader?!../../../assets/images/feather/flag.svg').default, 23 flag: require('!!raw-loader?!../../../assets/images/feather/flag.svg').default,
24 'playlists': require('!!raw-loader?!../../../assets/images/feather/list.svg').default, 24 playlists: require('!!raw-loader?!../../../assets/images/feather/list.svg').default,
25 'syndication': require('!!raw-loader?!../../../assets/images/feather/syndication.svg').default, 25 syndication: require('!!raw-loader?!../../../assets/images/feather/syndication.svg').default,
26 'help': require('!!raw-loader?!../../../assets/images/feather/help.svg').default, 26 help: require('!!raw-loader?!../../../assets/images/feather/help.svg').default,
27 'alert': require('!!raw-loader?!../../../assets/images/feather/alert.svg').default, 27 alert: require('!!raw-loader?!../../../assets/images/feather/alert.svg').default,
28 'globe': require('!!raw-loader?!../../../assets/images/feather/globe.svg').default, 28 globe: require('!!raw-loader?!../../../assets/images/feather/globe.svg').default,
29 'home': require('!!raw-loader?!../../../assets/images/feather/home.svg').default, 29 home: require('!!raw-loader?!../../../assets/images/feather/home.svg').default,
30 'recently-added': require('!!raw-loader?!../../../assets/images/feather/recently-added.svg').default, 30 'recently-added': require('!!raw-loader?!../../../assets/images/feather/recently-added.svg').default,
31 'trending': require('!!raw-loader?!../../../assets/images/feather/trending.svg').default, 31 trending: require('!!raw-loader?!../../../assets/images/feather/trending.svg').default,
32 'search': require('!!raw-loader?!../../../assets/images/feather/search.svg').default, 32 search: require('!!raw-loader?!../../../assets/images/feather/search.svg').default,
33 'upload': require('!!raw-loader?!../../../assets/images/feather/upload.svg').default, 33 upload: require('!!raw-loader?!../../../assets/images/feather/upload.svg').default,
34 'dislike': require('!!raw-loader?!../../../assets/images/feather/dislike.svg').default, 34 dislike: require('!!raw-loader?!../../../assets/images/feather/dislike.svg').default,
35 'like': require('!!raw-loader?!../../../assets/images/feather/like.svg').default, 35 like: require('!!raw-loader?!../../../assets/images/feather/like.svg').default,
36 'no': require('!!raw-loader?!../../../assets/images/feather/no.svg').default, 36 no: require('!!raw-loader?!../../../assets/images/feather/no.svg').default,
37 'cloud-download': require('!!raw-loader?!../../../assets/images/feather/cloud-download.svg').default, 37 'cloud-download': require('!!raw-loader?!../../../assets/images/feather/cloud-download.svg').default,
38 'clock': require('!!raw-loader?!../../../assets/images/feather/clock.svg').default, 38 clock: require('!!raw-loader?!../../../assets/images/feather/clock.svg').default,
39 'cog': require('!!raw-loader?!../../../assets/images/feather/cog.svg').default, 39 cog: require('!!raw-loader?!../../../assets/images/feather/cog.svg').default,
40 'delete': require('!!raw-loader?!../../../assets/images/feather/delete.svg').default, 40 delete: require('!!raw-loader?!../../../assets/images/feather/delete.svg').default,
41 'bell': require('!!raw-loader?!../../../assets/images/feather/bell.svg').default, 41 bell: require('!!raw-loader?!../../../assets/images/feather/bell.svg').default,
42 'sign-out': require('!!raw-loader?!../../../assets/images/feather/log-out.svg').default, 42 'sign-out': require('!!raw-loader?!../../../assets/images/feather/log-out.svg').default,
43 'sign-in': require('!!raw-loader?!../../../assets/images/feather/log-in.svg').default, 43 'sign-in': require('!!raw-loader?!../../../assets/images/feather/log-in.svg').default,
44 'download': require('!!raw-loader?!../../../assets/images/feather/download.svg').default, 44 download: require('!!raw-loader?!../../../assets/images/feather/download.svg').default,
45 'ownership-change': require('!!raw-loader?!../../../assets/images/feather/share.svg').default, 45 'ownership-change': require('!!raw-loader?!../../../assets/images/feather/share.svg').default,
46 'share': require('!!raw-loader?!../../../assets/images/feather/share-2.svg').default, 46 share: require('!!raw-loader?!../../../assets/images/feather/share-2.svg').default,
47 'channel': require('!!raw-loader?!../../../assets/images/feather/tv.svg').default, 47 channel: require('!!raw-loader?!../../../assets/images/feather/tv.svg').default,
48 'user': require('!!raw-loader?!../../../assets/images/feather/user.svg').default, 48 user: require('!!raw-loader?!../../../assets/images/feather/user.svg').default,
49 'user-x': require('!!raw-loader?!../../../assets/images/feather/user-x.svg').default, 49 'user-x': require('!!raw-loader?!../../../assets/images/feather/user-x.svg').default,
50 'users': require('!!raw-loader?!../../../assets/images/feather/users.svg').default, 50 users: require('!!raw-loader?!../../../assets/images/feather/users.svg').default,
51 'user-add': require('!!raw-loader?!../../../assets/images/feather/user-plus.svg').default, 51 'user-add': require('!!raw-loader?!../../../assets/images/feather/user-plus.svg').default,
52 'add': require('!!raw-loader?!../../../assets/images/feather/plus-circle.svg').default, 52 add: require('!!raw-loader?!../../../assets/images/feather/plus-circle.svg').default,
53 'cloud-error': require('!!raw-loader?!../../../assets/images/feather/cloud-off.svg').default, 53 'cloud-error': require('!!raw-loader?!../../../assets/images/feather/cloud-off.svg').default,
54 'undo': require('!!raw-loader?!../../../assets/images/feather/corner-up-left.svg').default, 54 undo: require('!!raw-loader?!../../../assets/images/feather/corner-up-left.svg').default,
55 'circle-tick': require('!!raw-loader?!../../../assets/images/feather/check-circle.svg').default, 55 'circle-tick': require('!!raw-loader?!../../../assets/images/feather/check-circle.svg').default,
56 'more-horizontal': require('!!raw-loader?!../../../assets/images/feather/more-horizontal.svg').default, 56 'more-horizontal': require('!!raw-loader?!../../../assets/images/feather/more-horizontal.svg').default,
57 'more-vertical': require('!!raw-loader?!../../../assets/images/feather/more-vertical.svg').default, 57 'more-vertical': require('!!raw-loader?!../../../assets/images/feather/more-vertical.svg').default,
58 'play': require('!!raw-loader?!../../../assets/images/feather/play.svg').default, 58 play: require('!!raw-loader?!../../../assets/images/feather/play.svg').default,
59 'p2p': require('!!raw-loader?!../../../assets/images/feather/airplay.svg').default, 59 p2p: require('!!raw-loader?!../../../assets/images/feather/airplay.svg').default,
60 'fullscreen': require('!!raw-loader?!../../../assets/images/feather/maximize.svg').default, 60 fullscreen: require('!!raw-loader?!../../../assets/images/feather/maximize.svg').default,
61 'exit-fullscreen': require('!!raw-loader?!../../../assets/images/feather/minimize.svg').default, 61 'exit-fullscreen': require('!!raw-loader?!../../../assets/images/feather/minimize.svg').default,
62 'film': require('!!raw-loader?!../../../assets/images/feather/film.svg').default, 62 film: require('!!raw-loader?!../../../assets/images/feather/film.svg').default,
63 'edit': require('!!raw-loader?!../../../assets/images/feather/edit-2.svg').default, 63 edit: require('!!raw-loader?!../../../assets/images/feather/edit-2.svg').default,
64 'sensitive': require('!!raw-loader?!../../../assets/images/feather/eye.svg').default, 64 sensitive: require('!!raw-loader?!../../../assets/images/feather/eye.svg').default,
65 'unsensitive': require('!!raw-loader?!../../../assets/images/feather/eye-off.svg').default, 65 unsensitive: require('!!raw-loader?!../../../assets/images/feather/eye-off.svg').default,
66 'refresh': require('!!raw-loader?!../../../assets/images/feather/refresh-cw.svg').default, 66 refresh: require('!!raw-loader?!../../../assets/images/feather/refresh-cw.svg').default,
67 'command': require('!!raw-loader?!../../../assets/images/feather/command.svg').default, 67 command: require('!!raw-loader?!../../../assets/images/feather/command.svg').default,
68 'go': require('!!raw-loader?!../../../assets/images/feather/arrow-up-right.svg').default, 68 go: require('!!raw-loader?!../../../assets/images/feather/arrow-up-right.svg').default,
69 'cross': require('!!raw-loader?!../../../assets/images/feather/x.svg').default, 69 cross: require('!!raw-loader?!../../../assets/images/feather/x.svg').default,
70 'tick': require('!!raw-loader?!../../../assets/images/feather/check.svg').default, 70 tick: require('!!raw-loader?!../../../assets/images/feather/check.svg').default,
71 'columns': require('!!raw-loader?!../../../assets/images/feather/columns.svg').default, 71 columns: require('!!raw-loader?!../../../assets/images/feather/columns.svg').default,
72 'live': require('!!raw-loader?!../../../assets/images/feather/live.svg').default, 72 live: require('!!raw-loader?!../../../assets/images/feather/live.svg').default,
73 'repeat': require('!!raw-loader?!../../../assets/images/feather/repeat.svg').default, 73 repeat: require('!!raw-loader?!../../../assets/images/feather/repeat.svg').default,
74 'message-circle': require('!!raw-loader?!../../../assets/images/feather/message-circle.svg').default, 74 'message-circle': require('!!raw-loader?!../../../assets/images/feather/message-circle.svg').default,
75 'codesandbox': require('!!raw-loader?!../../../assets/images/feather/codesandbox.svg').default, 75 codesandbox: require('!!raw-loader?!../../../assets/images/feather/codesandbox.svg').default,
76 'award': require('!!raw-loader?!../../../assets/images/feather/award.svg').default 76 award: require('!!raw-loader?!../../../assets/images/feather/award.svg').default
77} 77}
78 78
79export type GlobalIconName = keyof typeof icons 79export type GlobalIconName = keyof typeof icons
diff --git a/client/src/app/shared/shared-instance/instance-about-accordion.component.ts b/client/src/app/shared/shared-instance/instance-about-accordion.component.ts
index 855c06aa1..1eb7b49b6 100644
--- a/client/src/app/shared/shared-instance/instance-about-accordion.component.ts
+++ b/client/src/app/shared/shared-instance/instance-about-accordion.component.ts
@@ -7,7 +7,7 @@ import { About } from '@shared/models/server'
7@Component({ 7@Component({
8 selector: 'my-instance-about-accordion', 8 selector: 'my-instance-about-accordion',
9 templateUrl: './instance-about-accordion.component.html', 9 templateUrl: './instance-about-accordion.component.html',
10 styleUrls: ['./instance-about-accordion.component.scss'] 10 styleUrls: [ './instance-about-accordion.component.scss' ]
11}) 11})
12export class InstanceAboutAccordionComponent implements OnInit { 12export class InstanceAboutAccordionComponent implements OnInit {
13 @ViewChild('accordion', { static: true }) accordion: NgbAccordion 13 @ViewChild('accordion', { static: true }) accordion: NgbAccordion
diff --git a/client/src/app/shared/shared-instance/instance-follow.service.ts b/client/src/app/shared/shared-instance/instance-follow.service.ts
index af44020cf..a6799d3e1 100644
--- a/client/src/app/shared/shared-instance/instance-follow.service.ts
+++ b/client/src/app/shared/shared-instance/instance-follow.service.ts
@@ -19,10 +19,10 @@ export class InstanceFollowService {
19 } 19 }
20 20
21 getFollowing (options: { 21 getFollowing (options: {
22 pagination: RestPagination, 22 pagination: RestPagination
23 sort: SortMeta, 23 sort: SortMeta
24 search?: string, 24 search?: string
25 actorType?: ActivityPubActorType, 25 actorType?: ActivityPubActorType
26 state?: FollowState 26 state?: FollowState
27 }): Observable<ResultList<ActorFollow>> { 27 }): Observable<ResultList<ActorFollow>> {
28 const { pagination, sort, search, state, actorType } = options 28 const { pagination, sort, search, state, actorType } = options
@@ -42,10 +42,10 @@ export class InstanceFollowService {
42 } 42 }
43 43
44 getFollowers (options: { 44 getFollowers (options: {
45 pagination: RestPagination, 45 pagination: RestPagination
46 sort: SortMeta, 46 sort: SortMeta
47 search?: string, 47 search?: string
48 actorType?: ActivityPubActorType, 48 actorType?: ActivityPubActorType
49 state?: FollowState 49 state?: FollowState
50 }): Observable<ResultList<ActorFollow>> { 50 }): Observable<ResultList<ActorFollow>> {
51 const { pagination, sort, search, state, actorType } = options 51 const { pagination, sort, search, state, actorType } = options
diff --git a/client/src/app/shared/shared-instance/instance-statistics.component.ts b/client/src/app/shared/shared-instance/instance-statistics.component.ts
index 40aa8a4c0..0618efe69 100644
--- a/client/src/app/shared/shared-instance/instance-statistics.component.ts
+++ b/client/src/app/shared/shared-instance/instance-statistics.component.ts
@@ -17,6 +17,8 @@ export class InstanceStatisticsComponent implements OnInit {
17 17
18 ngOnInit () { 18 ngOnInit () {
19 this.serverService.getServerStats() 19 this.serverService.getServerStats()
20 .subscribe(res => this.serverStats = res) 20 .subscribe(res => {
21 this.serverStats = res
22 })
21 } 23 }
22} 24}
diff --git a/client/src/app/shared/shared-instance/instance.service.ts b/client/src/app/shared/shared-instance/instance.service.ts
index 70e022178..0241f56ef 100644
--- a/client/src/app/shared/shared-instance/instance.service.ts
+++ b/client/src/app/shared/shared-instance/instance.service.ts
@@ -51,7 +51,7 @@ export class InstanceService {
51 } 51 }
52 52
53 for (const key of Object.keys(html)) { 53 for (const key of Object.keys(html)) {
54 html[ key ] = await this.markdownService.textMarkdownToHTML(about.instance[ key ]) 54 html[key] = await this.markdownService.textMarkdownToHTML(about.instance[key])
55 } 55 }
56 56
57 return html 57 return html
diff --git a/client/src/app/shared/shared-instance/shared-instance.module.ts b/client/src/app/shared/shared-instance/shared-instance.module.ts
index 5efb95a7d..13c681ab8 100644
--- a/client/src/app/shared/shared-instance/shared-instance.module.ts
+++ b/client/src/app/shared/shared-instance/shared-instance.module.ts
@@ -1,7 +1,6 @@
1 1
2import { NgModule } from '@angular/core' 2import { NgModule } from '@angular/core'
3import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap' 3import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
4import { SharedCustomMarkupModule } from '../shared-custom-markup'
5import { SharedMainModule } from '../shared-main/shared-main.module' 4import { SharedMainModule } from '../shared-main/shared-main.module'
6import { FeatureBooleanComponent } from './feature-boolean.component' 5import { FeatureBooleanComponent } from './feature-boolean.component'
7import { InstanceAboutAccordionComponent } from './instance-about-accordion.component' 6import { InstanceAboutAccordionComponent } from './instance-about-accordion.component'
diff --git a/client/src/app/shared/shared-main/account/account.model.ts b/client/src/app/shared/shared-main/account/account.model.ts
index 7b5611f35..92606e7fa 100644
--- a/client/src/app/shared/shared-main/account/account.model.ts
+++ b/client/src/app/shared/shared-main/account/account.model.ts
@@ -1,4 +1,4 @@
1import { Account as ServerAccount, Actor as ServerActor, ActorImage } from '@shared/models' 1import { Account as ServerAccount, ActorImage } from '@shared/models'
2import { Actor } from './actor.model' 2import { Actor } from './actor.model'
3 3
4export class Account extends Actor implements ServerAccount { 4export class Account extends Actor implements ServerAccount {
diff --git a/client/src/app/shared/shared-main/account/actor.model.ts b/client/src/app/shared/shared-main/account/actor.model.ts
index 2fccc472a..082f44fb9 100644
--- a/client/src/app/shared/shared-main/account/actor.model.ts
+++ b/client/src/app/shared/shared-main/account/actor.model.ts
@@ -20,7 +20,7 @@ export abstract class Actor implements ServerActor {
20 static GET_ACTOR_AVATAR_URL (actor: { avatar?: { url?: string, path: string } }) { 20 static GET_ACTOR_AVATAR_URL (actor: { avatar?: { url?: string, path: string } }) {
21 if (actor?.avatar?.url) return actor.avatar.url 21 if (actor?.avatar?.url) return actor.avatar.url
22 22
23 if (actor && actor.avatar) { 23 if (actor?.avatar) {
24 const absoluteAPIUrl = getAbsoluteAPIUrl() 24 const absoluteAPIUrl = getAbsoluteAPIUrl()
25 25
26 return absoluteAPIUrl + actor.avatar.path 26 return absoluteAPIUrl + actor.avatar.path
diff --git a/client/src/app/shared/shared-main/angular/autofocus.directive.ts b/client/src/app/shared/shared-main/angular/autofocus.directive.ts
index 5f087d79d..2da492ea1 100644
--- a/client/src/app/shared/shared-main/angular/autofocus.directive.ts
+++ b/client/src/app/shared/shared-main/angular/autofocus.directive.ts
@@ -1,7 +1,7 @@
1import { AfterViewInit, Directive, ElementRef } from '@angular/core' 1import { AfterViewInit, Directive, ElementRef } from '@angular/core'
2 2
3@Directive({ 3@Directive({
4 selector: '[autofocus]' 4 selector: '[myAutofocus]'
5}) 5})
6export class AutofocusDirective implements AfterViewInit { 6export class AutofocusDirective implements AfterViewInit {
7 constructor (private host: ElementRef) { } 7 constructor (private host: ElementRef) { }
diff --git a/client/src/app/shared/shared-main/angular/link.component.ts b/client/src/app/shared/shared-main/angular/link.component.ts
index 597a16871..ecbd9151c 100644
--- a/client/src/app/shared/shared-main/angular/link.component.ts
+++ b/client/src/app/shared/shared-main/angular/link.component.ts
@@ -1,4 +1,4 @@
1import { Component, Input, ViewEncapsulation } from '@angular/core' 1import { Component, Input } from '@angular/core'
2 2
3@Component({ 3@Component({
4 selector: 'my-link', 4 selector: 'my-link',
diff --git a/client/src/app/shared/shared-main/angular/peertube-template.directive.ts b/client/src/app/shared/shared-main/angular/peertube-template.directive.ts
index e04c25d9a..dc0cde12d 100644
--- a/client/src/app/shared/shared-main/angular/peertube-template.directive.ts
+++ b/client/src/app/shared/shared-main/angular/peertube-template.directive.ts
@@ -1,6 +1,7 @@
1import { Directive, Input, TemplateRef } from '@angular/core' 1import { Directive, Input, TemplateRef } from '@angular/core'
2 2
3@Directive({ 3@Directive({
4 // eslint-disable-next-line @angular-eslint/directive-selector
4 selector: '[ptTemplate]' 5 selector: '[ptTemplate]'
5}) 6})
6export class PeerTubeTemplateDirective <T extends string> { 7export class PeerTubeTemplateDirective <T extends string> {
diff --git a/client/src/app/shared/shared-main/feeds/syndication.model.ts b/client/src/app/shared/shared-main/feeds/syndication.model.ts
index 2466ae7c6..cd6fbdb48 100644
--- a/client/src/app/shared/shared-main/feeds/syndication.model.ts
+++ b/client/src/app/shared/shared-main/feeds/syndication.model.ts
@@ -1,7 +1,7 @@
1import { FeedFormat } from '@shared/models' 1import { FeedFormat } from '@shared/models'
2 2
3export interface Syndication { 3export interface Syndication {
4 format: FeedFormat, 4 format: FeedFormat
5 label: string, 5 label: string
6 url: string 6 url: string
7} 7}
diff --git a/client/src/app/shared/shared-main/misc/help.component.ts b/client/src/app/shared/shared-main/misc/help.component.ts
index 76e255d99..37e2abd97 100644
--- a/client/src/app/shared/shared-main/misc/help.component.ts
+++ b/client/src/app/shared/shared-main/misc/help.component.ts
@@ -71,18 +71,18 @@ export class HelpComponent implements OnInit, OnChanges, AfterContentInit {
71 } 71 }
72 72
73 private formatMarkdownSupport (rules: string[]) { 73 private formatMarkdownSupport (rules: string[]) {
74 // tslint:disable:max-line-length 74 /* eslint-disable max-len */
75 return $localize`<a href="https://en.wikipedia.org/wiki/Markdown#Example" target="_blank" rel="noopener noreferrer">Markdown</a> compatible that supports:` + 75 return $localize`<a href="https://en.wikipedia.org/wiki/Markdown#Example" target="_blank" rel="noopener noreferrer">Markdown</a> compatible that supports:` +
76 this.createMarkdownList(rules) 76 this.createMarkdownList(rules)
77 } 77 }
78 78
79 private createMarkdownList (rules: string[]) { 79 private createMarkdownList (rules: string[]) {
80 const rulesToText = { 80 const rulesToText = {
81 'emphasis': $localize`Emphasis`, 81 emphasis: $localize`Emphasis`,
82 'link': $localize`Links`, 82 link: $localize`Links`,
83 'newline': $localize`New lines`, 83 newline: $localize`New lines`,
84 'list': $localize`Lists`, 84 list: $localize`Lists`,
85 'image': $localize`Images` 85 image: $localize`Images`
86 } 86 }
87 87
88 const bullets = rules.map(r => rulesToText[r]) 88 const bullets = rules.map(r => rulesToText[r])
diff --git a/client/src/app/shared/shared-main/misc/list-overflow.component.ts b/client/src/app/shared/shared-main/misc/list-overflow.component.ts
index 144e0f156..fbc481093 100644
--- a/client/src/app/shared/shared-main/misc/list-overflow.component.ts
+++ b/client/src/app/shared/shared-main/misc/list-overflow.component.ts
@@ -22,7 +22,7 @@ export interface ListOverflowItem {
22} 22}
23 23
24@Component({ 24@Component({
25 selector: 'list-overflow', 25 selector: 'my-list-overflow',
26 templateUrl: './list-overflow.component.html', 26 templateUrl: './list-overflow.component.html',
27 styleUrls: [ './list-overflow.component.scss' ], 27 styleUrls: [ './list-overflow.component.scss' ],
28 changeDetection: ChangeDetectionStrategy.OnPush 28 changeDetection: ChangeDetectionStrategy.OnPush
@@ -65,7 +65,7 @@ export class ListOverflowComponent<T extends ListOverflowItem> implements AfterV
65 let showItemsUntilIndexExcluded: number 65 let showItemsUntilIndexExcluded: number
66 let accWidth = 0 66 let accWidth = 0
67 67
68 for (const [index, el] of this.itemsRendered.toArray().entries()) { 68 for (const [ index, el ] of this.itemsRendered.toArray().entries()) {
69 accWidth += el.nativeElement.getBoundingClientRect().width 69 accWidth += el.nativeElement.getBoundingClientRect().width
70 if (showItemsUntilIndexExcluded === undefined) { 70 if (showItemsUntilIndexExcluded === undefined) {
71 showItemsUntilIndexExcluded = (parentWidth < accWidth) ? index : undefined 71 showItemsUntilIndexExcluded = (parentWidth < accWidth) ? index : undefined
diff --git a/client/src/app/shared/shared-main/misc/simple-search-input.component.ts b/client/src/app/shared/shared-main/misc/simple-search-input.component.ts
index 224d71134..292ec4c82 100644
--- a/client/src/app/shared/shared-main/misc/simple-search-input.component.ts
+++ b/client/src/app/shared/shared-main/misc/simple-search-input.component.ts
@@ -4,7 +4,7 @@ import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild }
4import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
5 5
6@Component({ 6@Component({
7 selector: 'simple-search-input', 7 selector: 'my-simple-search-input',
8 templateUrl: './simple-search-input.component.html', 8 templateUrl: './simple-search-input.component.html',
9 styleUrls: [ './simple-search-input.component.scss' ] 9 styleUrls: [ './simple-search-input.component.scss' ]
10}) 10})
diff --git a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.ts b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.ts
index 2cafb6c55..e7e34ce1e 100644
--- a/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.ts
+++ b/client/src/app/shared/shared-main/misc/top-menu-dropdown.component.ts
@@ -66,7 +66,7 @@ export class TopMenuDropdownComponent implements OnInit, OnDestroy {
66 .subscribe(() => this.updateChildLabels(window.location.pathname)) 66 .subscribe(() => this.updateChildLabels(window.location.pathname))
67 67
68 this.hasIcons = this.menuEntries.some( 68 this.hasIcons = this.menuEntries.some(
69 e => e.children && e.children.some(c => !!c.iconName) 69 e => e.children?.some(c => !!c.iconName)
70 ) 70 )
71 } 71 }
72 72
diff --git a/client/src/app/shared/shared-main/users/user-notification.service.ts b/client/src/app/shared/shared-main/users/user-notification.service.ts
index 9014b48a8..09fee87a3 100644
--- a/client/src/app/shared/shared-main/users/user-notification.service.ts
+++ b/client/src/app/shared/shared-main/users/user-notification.service.ts
@@ -1,11 +1,11 @@
1import { SortMeta } from 'primeng/api'
1import { catchError, map, tap } from 'rxjs/operators' 2import { catchError, map, tap } from 'rxjs/operators'
2import { HttpClient, HttpParams } from '@angular/common/http' 3import { HttpClient, HttpParams } from '@angular/common/http'
3import { Injectable } from '@angular/core' 4import { Injectable } from '@angular/core'
4import { ComponentPaginationLight, RestExtractor, RestService, User, PeerTubeSocket, AuthService } from '@app/core' 5import { AuthService, ComponentPaginationLight, PeerTubeSocket, RestExtractor, RestService } from '@app/core'
5import { ResultList, UserNotification as UserNotificationServer, UserNotificationSetting } from '@shared/models' 6import { ResultList, UserNotification as UserNotificationServer, UserNotificationSetting } from '@shared/models'
6import { environment } from '../../../../environments/environment' 7import { environment } from '../../../../environments/environment'
7import { UserNotification } from './user-notification.model' 8import { UserNotification } from './user-notification.model'
8import { SortMeta } from 'primeng/api'
9 9
10@Injectable() 10@Injectable()
11export class UserNotificationService { 11export class UserNotificationService {
@@ -23,7 +23,7 @@ export class UserNotificationService {
23 listMyNotifications (parameters: { 23 listMyNotifications (parameters: {
24 pagination: ComponentPaginationLight 24 pagination: ComponentPaginationLight
25 ignoreLoadingBar?: boolean 25 ignoreLoadingBar?: boolean
26 unread?: boolean, 26 unread?: boolean
27 sort?: SortMeta 27 sort?: SortMeta
28 }) { 28 }) {
29 const { pagination, ignoreLoadingBar, unread, sort } = parameters 29 const { pagination, ignoreLoadingBar, unread, sort } = parameters
diff --git a/client/src/app/shared/shared-main/users/user-quota.component.ts b/client/src/app/shared/shared-main/users/user-quota.component.ts
index b38619186..5a95f1209 100644
--- a/client/src/app/shared/shared-main/users/user-quota.component.ts
+++ b/client/src/app/shared/shared-main/users/user-quota.component.ts
@@ -6,7 +6,7 @@ import { BytesPipe } from '../angular'
6@Component({ 6@Component({
7 selector: 'my-user-quota', 7 selector: 'my-user-quota',
8 templateUrl: './user-quota.component.html', 8 templateUrl: './user-quota.component.html',
9 styleUrls: ['./user-quota.component.scss'] 9 styleUrls: [ './user-quota.component.scss' ]
10}) 10})
11 11
12export class UserQuotaComponent implements OnInit { 12export class UserQuotaComponent implements OnInit {
diff --git a/client/src/app/shared/shared-main/video-channel/video-channel.model.ts b/client/src/app/shared/shared-main/video-channel/video-channel.model.ts
index a9dcf2fa2..66d4cac68 100644
--- a/client/src/app/shared/shared-main/video-channel/video-channel.model.ts
+++ b/client/src/app/shared/shared-main/video-channel/video-channel.model.ts
@@ -1,6 +1,5 @@
1import { getAbsoluteAPIUrl } from '@app/helpers' 1import { getAbsoluteAPIUrl } from '@app/helpers'
2import { Account as ServerAccount, ActorImage, VideoChannel as ServerVideoChannel, ViewsPerDate } from '@shared/models' 2import { Account as ServerAccount, ActorImage, VideoChannel as ServerVideoChannel, ViewsPerDate } from '@shared/models'
3import { Account } from '../account/account.model'
4import { Actor } from '../account/actor.model' 3import { Actor } from '../account/actor.model'
5 4
6export class VideoChannel extends Actor implements ServerVideoChannel { 5export class VideoChannel extends Actor implements ServerVideoChannel {
@@ -25,14 +24,14 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
25 24
26 viewsPerDay?: ViewsPerDate[] 25 viewsPerDay?: ViewsPerDate[]
27 26
28 static GET_ACTOR_AVATAR_URL (actor: object) { 27 static GET_ACTOR_AVATAR_URL (actor: { avatar?: { url?: string, path: string } }) {
29 return Actor.GET_ACTOR_AVATAR_URL(actor) 28 return Actor.GET_ACTOR_AVATAR_URL(actor)
30 } 29 }
31 30
32 static GET_ACTOR_BANNER_URL (channel: ServerVideoChannel) { 31 static GET_ACTOR_BANNER_URL (channel: ServerVideoChannel) {
33 if (channel?.banner?.url) return channel.banner.url 32 if (channel?.banner?.url) return channel.banner.url
34 33
35 if (channel && channel.banner) { 34 if (channel?.banner) {
36 const absoluteAPIUrl = getAbsoluteAPIUrl() 35 const absoluteAPIUrl = getAbsoluteAPIUrl()
37 36
38 return absoluteAPIUrl + channel.banner.path 37 return absoluteAPIUrl + channel.banner.path
diff --git a/client/src/app/shared/shared-main/video-channel/video-channel.service.ts b/client/src/app/shared/shared-main/video-channel/video-channel.service.ts
index a89f1065a..7560a35a8 100644
--- a/client/src/app/shared/shared-main/video-channel/video-channel.service.ts
+++ b/client/src/app/shared/shared-main/video-channel/video-channel.service.ts
@@ -15,6 +15,12 @@ export class VideoChannelService {
15 15
16 videoChannelLoaded = new ReplaySubject<VideoChannel>(1) 16 videoChannelLoaded = new ReplaySubject<VideoChannel>(1)
17 17
18 constructor (
19 private authHttp: HttpClient,
20 private restService: RestService,
21 private restExtractor: RestExtractor
22 ) { }
23
18 static extractVideoChannels (result: ResultList<VideoChannelServer>) { 24 static extractVideoChannels (result: ResultList<VideoChannelServer>) {
19 const videoChannels: VideoChannel[] = [] 25 const videoChannels: VideoChannel[] = []
20 26
@@ -25,12 +31,6 @@ export class VideoChannelService {
25 return { data: videoChannels, total: result.total } 31 return { data: videoChannels, total: result.total }
26 } 32 }
27 33
28 constructor (
29 private authHttp: HttpClient,
30 private restService: RestService,
31 private restExtractor: RestExtractor
32 ) { }
33
34 getVideoChannel (videoChannelName: string) { 34 getVideoChannel (videoChannelName: string) {
35 return this.authHttp.get<VideoChannel>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelName) 35 return this.authHttp.get<VideoChannel>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelName)
36 .pipe( 36 .pipe(
diff --git a/client/src/app/shared/shared-main/video/redundancy.service.ts b/client/src/app/shared/shared-main/video/redundancy.service.ts
index 6e839e655..966d7fafd 100644
--- a/client/src/app/shared/shared-main/video/redundancy.service.ts
+++ b/client/src/app/shared/shared-main/video/redundancy.service.ts
@@ -30,8 +30,8 @@ export class RedundancyService {
30 } 30 }
31 31
32 listVideoRedundancies (options: { 32 listVideoRedundancies (options: {
33 pagination: RestPagination, 33 pagination: RestPagination
34 sort: SortMeta, 34 sort: SortMeta
35 target?: VideoRedundanciesTarget 35 target?: VideoRedundanciesTarget
36 }): Observable<ResultList<VideoRedundancy>> { 36 }): Observable<ResultList<VideoRedundancy>> {
37 const { pagination, sort, target } = options 37 const { pagination, sort, target } = options
diff --git a/client/src/app/shared/shared-main/video/video-edit.model.ts b/client/src/app/shared/shared-main/video/video-edit.model.ts
index 757b686c0..ea0456942 100644
--- a/client/src/app/shared/shared-main/video/video-edit.model.ts
+++ b/client/src/app/shared/shared-main/video/video-edit.model.ts
@@ -29,11 +29,11 @@ export class VideoEdit implements VideoUpdate {
29 29
30 constructor ( 30 constructor (
31 video?: Video & { 31 video?: Video & {
32 tags: string[], 32 tags: string[]
33 commentsEnabled: boolean, 33 commentsEnabled: boolean
34 downloadEnabled: boolean, 34 downloadEnabled: boolean
35 support: string, 35 support: string
36 thumbnailUrl: string, 36 thumbnailUrl: string
37 previewUrl: string 37 previewUrl: string
38 }) { 38 }) {
39 if (video) { 39 if (video) {
@@ -64,7 +64,7 @@ export class VideoEdit implements VideoUpdate {
64 64
65 patch (values: { [ id: string ]: any }) { 65 patch (values: { [ id: string ]: any }) {
66 Object.keys(values).forEach((key) => { 66 Object.keys(values).forEach((key) => {
67 this[ key ] = values[ key ] 67 this[key] = values[key]
68 }) 68 })
69 69
70 // If schedule publication, the video is private and will be changed to public privacy 70 // If schedule publication, the video is private and will be changed to public privacy
diff --git a/client/src/app/shared/shared-main/video/video.model.ts b/client/src/app/shared/shared-main/video/video.model.ts
index b7720c8d2..7471a933b 100644
--- a/client/src/app/shared/shared-main/video/video.model.ts
+++ b/client/src/app/shared/shared-main/video/video.model.ts
@@ -100,7 +100,7 @@ export class Video implements VideoServerModel {
100 return '/videos/update/' + video.uuid 100 return '/videos/update/' + video.uuid
101 } 101 }
102 102
103 constructor (hash: VideoServerModel, translations = {}) { 103 constructor (hash: VideoServerModel, translations: { [ id: string ]: string } = {}) {
104 const absoluteAPIUrl = getAbsoluteAPIUrl() 104 const absoluteAPIUrl = getAbsoluteAPIUrl()
105 105
106 this.createdAt = new Date(hash.createdAt.toString()) 106 this.createdAt = new Date(hash.createdAt.toString())
diff --git a/client/src/app/shared/shared-main/video/video.service.ts b/client/src/app/shared/shared-main/video/video.service.ts
index 4a97719fa..60cc9d160 100644
--- a/client/src/app/shared/shared-main/video/video.service.ts
+++ b/client/src/app/shared/shared-main/video/video.service.ts
@@ -30,10 +30,10 @@ import { Video } from './video.model'
30 30
31export interface VideosProvider { 31export interface VideosProvider {
32 getVideos (parameters: { 32 getVideos (parameters: {
33 videoPagination: ComponentPaginationLight, 33 videoPagination: ComponentPaginationLight
34 sort: VideoSortField, 34 sort: VideoSortField
35 filter?: VideoFilter, 35 filter?: VideoFilter
36 categoryOneOf?: number[], 36 categoryOneOf?: number[]
37 languageOneOf?: string[] 37 languageOneOf?: string[]
38 nsfwPolicy: NSFWPolicyType 38 nsfwPolicy: NSFWPolicyType
39 }): Observable<ResultList<Video>> 39 }): Observable<ResultList<Video>>
@@ -145,8 +145,8 @@ export class VideoService implements VideosProvider {
145 } 145 }
146 146
147 getAccountVideos (parameters: { 147 getAccountVideos (parameters: {
148 account: Pick<Account, 'nameWithHost'>, 148 account: Pick<Account, 'nameWithHost'>
149 videoPagination: ComponentPaginationLight, 149 videoPagination: ComponentPaginationLight
150 sort: VideoSortField 150 sort: VideoSortField
151 nsfwPolicy?: NSFWPolicyType 151 nsfwPolicy?: NSFWPolicyType
152 videoFilter?: VideoFilter 152 videoFilter?: VideoFilter
@@ -180,9 +180,9 @@ export class VideoService implements VideosProvider {
180 } 180 }
181 181
182 getVideoChannelVideos (parameters: { 182 getVideoChannelVideos (parameters: {
183 videoChannel: Pick<VideoChannel, 'nameWithHost'>, 183 videoChannel: Pick<VideoChannel, 'nameWithHost'>
184 videoPagination: ComponentPaginationLight, 184 videoPagination: ComponentPaginationLight
185 sort: VideoSortField, 185 sort: VideoSortField
186 nsfwPolicy?: NSFWPolicyType 186 nsfwPolicy?: NSFWPolicyType
187 videoFilter?: VideoFilter 187 videoFilter?: VideoFilter
188 }): Observable<ResultList<Video>> { 188 }): Observable<ResultList<Video>> {
diff --git a/client/src/app/shared/shared-moderation/abuse.service.ts b/client/src/app/shared/shared-moderation/abuse.service.ts
index bf98d4b36..f45b5c8e8 100644
--- a/client/src/app/shared/shared-moderation/abuse.service.ts
+++ b/client/src/app/shared/shared-moderation/abuse.service.ts
@@ -30,8 +30,8 @@ export class AbuseService {
30 ) { } 30 ) { }
31 31
32 getAdminAbuses (options: { 32 getAdminAbuses (options: {
33 pagination: RestPagination, 33 pagination: RestPagination
34 sort: SortMeta, 34 sort: SortMeta
35 search?: string 35 search?: string
36 }): Observable<ResultList<AdminAbuse>> { 36 }): Observable<ResultList<AdminAbuse>> {
37 const { pagination, sort, search } = options 37 const { pagination, sort, search } = options
@@ -51,8 +51,8 @@ export class AbuseService {
51 } 51 }
52 52
53 getUserAbuses (options: { 53 getUserAbuses (options: {
54 pagination: RestPagination, 54 pagination: RestPagination
55 sort: SortMeta, 55 sort: SortMeta
56 search?: string 56 search?: string
57 }): Observable<ResultList<UserAbuse>> { 57 }): Observable<ResultList<UserAbuse>> {
58 const { pagination, sort, search } = options 58 const { pagination, sort, search } = options
@@ -74,7 +74,7 @@ export class AbuseService {
74 reportVideo (parameters: AbuseCreate) { 74 reportVideo (parameters: AbuseCreate) {
75 const url = AbuseService.BASE_ABUSE_URL 75 const url = AbuseService.BASE_ABUSE_URL
76 76
77 const body = omit(parameters, ['id']) 77 const body = omit(parameters, [ 'id' ])
78 78
79 return this.authHttp.post(url, body) 79 return this.authHttp.post(url, body)
80 .pipe( 80 .pipe(
@@ -147,11 +147,13 @@ export class AbuseService {
147 { 147 {
148 id: 'spamOrMisleading', 148 id: 'spamOrMisleading',
149 label: $localize`Spam, ad or false news`, 149 label: $localize`Spam, ad or false news`,
150 // eslint-disable-next-line max-len
150 help: $localize`Contains marketing, spam, purposefully deceitful news, or otherwise misleading thumbnail/text/tags. Please provide reputable sources to report hoaxes.` 151 help: $localize`Contains marketing, spam, purposefully deceitful news, or otherwise misleading thumbnail/text/tags. Please provide reputable sources to report hoaxes.`
151 }, 152 },
152 { 153 {
153 id: 'privacy', 154 id: 'privacy',
154 label: $localize`Privacy breach or doxxing`, 155 label: $localize`Privacy breach or doxxing`,
156 // eslint-disable-next-line max-len
155 help: $localize`Contains personal information that could be used to track, identify, contact or impersonate someone (e.g. name, address, phone number, email, or credit card details).` 157 help: $localize`Contains personal information that could be used to track, identify, contact or impersonate someone (e.g. name, address, phone number, email, or credit card details).`
156 }, 158 },
157 { 159 {
@@ -162,6 +164,7 @@ export class AbuseService {
162 { 164 {
163 id: 'serverRules', 165 id: 'serverRules',
164 label: $localize`Breaks server rules`, 166 label: $localize`Breaks server rules`,
167 // eslint-disable-next-line max-len
165 description: $localize`Anything not included in the above that breaks the terms of service, code of conduct, or general rules in place on the server.` 168 description: $localize`Anything not included in the above that breaks the terms of service, code of conduct, or general rules in place on the server.`
166 } 169 }
167 ] 170 ]
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.ts b/client/src/app/shared/shared-moderation/account-blocklist.component.ts
index 36f2a591b..9ed00bc12 100644
--- a/client/src/app/shared/shared-moderation/account-blocklist.component.ts
+++ b/client/src/app/shared/shared-moderation/account-blocklist.component.ts
@@ -1,14 +1,13 @@
1import { SortMeta } from 'primeng/api' 1import { SortMeta } from 'primeng/api'
2import { Directive, OnInit } from '@angular/core' 2import { Directive, OnInit } from '@angular/core'
3import { Notifier, RestPagination, RestTable } from '@app/core' 3import { Notifier, RestPagination, RestTable } from '@app/core'
4import { Account } from '@app/shared/shared-main'
5import { AccountBlock } from './account-block.model' 4import { AccountBlock } from './account-block.model'
6import { BlocklistComponentType, BlocklistService } from './blocklist.service' 5import { BlocklistComponentType, BlocklistService } from './blocklist.service'
7 6
8@Directive() 7@Directive()
9// tslint:disable-next-line: directive-class-suffix 8// eslint-disable-next-line @angular-eslint/directive-class-suffix
10export class GenericAccountBlocklistComponent extends RestTable implements OnInit { 9export class GenericAccountBlocklistComponent extends RestTable implements OnInit {
11 // @ts-ignore: "Abstract methods can only appear within an abstract class" 10 // @ts-expect-error: "Abstract methods can only appear within an abstract class"
12 abstract mode: BlocklistComponentType 11 abstract mode: BlocklistComponentType
13 12
14 blockedAccounts: AccountBlock[] = [] 13 blockedAccounts: AccountBlock[] = []
@@ -23,7 +22,7 @@ export class GenericAccountBlocklistComponent extends RestTable implements OnIni
23 super() 22 super()
24 } 23 }
25 24
26 // @ts-ignore: "Abstract methods can only appear within an abstract class" 25 // @ts-expect-error: "Abstract methods can only appear within an abstract class"
27 abstract getIdentifier (): string 26 abstract getIdentifier (): string
28 27
29 ngOnInit () { 28 ngOnInit () {
diff --git a/client/src/app/shared/shared-moderation/blocklist.service.ts b/client/src/app/shared/shared-moderation/blocklist.service.ts
index de677a77b..db2a8c584 100644
--- a/client/src/app/shared/shared-moderation/blocklist.service.ts
+++ b/client/src/app/shared/shared-moderation/blocklist.service.ts
@@ -21,7 +21,7 @@ export class BlocklistService {
21 private restService: RestService 21 private restService: RestService
22 ) { } 22 ) { }
23 23
24 /*********************** User -> Account blocklist ***********************/ 24 /** ********************* User -> Account blocklist ***********************/
25 25
26 getUserAccountBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) { 26 getUserAccountBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) {
27 const { pagination, sort, search } = options 27 const { pagination, sort, search } = options
@@ -53,7 +53,7 @@ export class BlocklistService {
53 .pipe(catchError(err => this.restExtractor.handleError(err))) 53 .pipe(catchError(err => this.restExtractor.handleError(err)))
54 } 54 }
55 55
56 /*********************** User -> Server blocklist ***********************/ 56 /** ********************* User -> Server blocklist ***********************/
57 57
58 getUserServerBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) { 58 getUserServerBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) {
59 const { pagination, sort, search } = options 59 const { pagination, sort, search } = options
@@ -84,7 +84,7 @@ export class BlocklistService {
84 .pipe(catchError(err => this.restExtractor.handleError(err))) 84 .pipe(catchError(err => this.restExtractor.handleError(err)))
85 } 85 }
86 86
87 /*********************** Instance -> Account blocklist ***********************/ 87 /** ********************* Instance -> Account blocklist ***********************/
88 88
89 getInstanceAccountBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) { 89 getInstanceAccountBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) {
90 const { pagination, sort, search } = options 90 const { pagination, sort, search } = options
@@ -116,7 +116,7 @@ export class BlocklistService {
116 .pipe(catchError(err => this.restExtractor.handleError(err))) 116 .pipe(catchError(err => this.restExtractor.handleError(err)))
117 } 117 }
118 118
119 /*********************** Instance -> Server blocklist ***********************/ 119 /** ********************* Instance -> Server blocklist ***********************/
120 120
121 getInstanceServerBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) { 121 getInstanceServerBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) {
122 const { pagination, sort, search } = options 122 const { pagination, sort, search } = options
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.ts b/client/src/app/shared/shared-moderation/server-blocklist.component.ts
index da09b1d0e..1ba7a1b4d 100644
--- a/client/src/app/shared/shared-moderation/server-blocklist.component.ts
+++ b/client/src/app/shared/shared-moderation/server-blocklist.component.ts
@@ -6,11 +6,11 @@ import { ServerBlock } from '@shared/models'
6import { BlocklistComponentType, BlocklistService } from './blocklist.service' 6import { BlocklistComponentType, BlocklistService } from './blocklist.service'
7 7
8@Directive() 8@Directive()
9// tslint:disable-next-line: directive-class-suffix 9// eslint-disable-next-line @angular-eslint/directive-class-suffix
10export class GenericServerBlocklistComponent extends RestTable implements OnInit { 10export class GenericServerBlocklistComponent extends RestTable implements OnInit {
11 @ViewChild('batchDomainsModal') batchDomainsModal: BatchDomainsModalComponent 11 @ViewChild('batchDomainsModal') batchDomainsModal: BatchDomainsModalComponent
12 12
13 // @ts-ignore: "Abstract methods can only appear within an abstract class" 13 // @ts-expect-error: "Abstract methods can only appear within an abstract class"
14 public abstract mode: BlocklistComponentType 14 public abstract mode: BlocklistComponentType
15 15
16 blockedServers: ServerBlock[] = [] 16 blockedServers: ServerBlock[] = []
@@ -25,7 +25,7 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit
25 super() 25 super()
26 } 26 }
27 27
28 // @ts-ignore: "Abstract methods can only appear within an abstract class" 28 // @ts-expect-error: "Abstract methods can only appear within an abstract class"
29 public abstract getIdentifier (): string 29 public abstract getIdentifier (): string
30 30
31 ngOnInit () { 31 ngOnInit () {
@@ -34,8 +34,8 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit
34 34
35 unblockServer (serverBlock: ServerBlock) { 35 unblockServer (serverBlock: ServerBlock) {
36 const operation = (host: string) => this.mode === BlocklistComponentType.Account 36 const operation = (host: string) => this.mode === BlocklistComponentType.Account
37 ? this.blocklistService.unblockServerByUser(host) 37 ? this.blocklistService.unblockServerByUser(host)
38 : this.blocklistService.unblockServerByInstance(host) 38 : this.blocklistService.unblockServerByInstance(host)
39 const host = serverBlock.blockedServer.host 39 const host = serverBlock.blockedServer.host
40 40
41 operation(host).subscribe( 41 operation(host).subscribe(
diff --git a/client/src/app/shared/shared-moderation/video-block.component.ts b/client/src/app/shared/shared-moderation/video-block.component.ts
index bc6952620..f6c29dcfa 100644
--- a/client/src/app/shared/shared-moderation/video-block.component.ts
+++ b/client/src/app/shared/shared-moderation/video-block.component.ts
@@ -51,8 +51,8 @@ export class VideoBlockComponent extends FormReactive implements OnInit {
51 } 51 }
52 52
53 block () { 53 block () {
54 const reason = this.form.value[ 'reason' ] || undefined 54 const reason = this.form.value['reason'] || undefined
55 const unfederate = this.video.isLocal ? this.form.value[ 'unfederate' ] : undefined 55 const unfederate = this.video.isLocal ? this.form.value['unfederate'] : undefined
56 56
57 this.videoBlocklistService.blockVideo(this.video.id, reason, unfederate) 57 this.videoBlocklistService.blockVideo(this.video.id, reason, unfederate)
58 .subscribe({ 58 .subscribe({
diff --git a/client/src/app/shared/shared-support-modal/support-modal.component.ts b/client/src/app/shared/shared-support-modal/support-modal.component.ts
index a0b9fada6..08e997f7b 100644
--- a/client/src/app/shared/shared-support-modal/support-modal.component.ts
+++ b/client/src/app/shared/shared-support-modal/support-modal.component.ts
@@ -28,7 +28,9 @@ export class SupportModalComponent {
28 const support = this.video?.support || this.videoChannel.support 28 const support = this.video?.support || this.videoChannel.support
29 29
30 this.markdownService.enhancedMarkdownToHTML(support) 30 this.markdownService.enhancedMarkdownToHTML(support)
31 .then(r => this.htmlSupport = r) 31 .then(r => {
32 this.htmlSupport = r
33 })
32 34
33 this.displayName = this.video 35 this.displayName = this.video
34 ? this.video.channel.displayName 36 ? this.video.channel.displayName
diff --git a/client/src/app/shared/shared-user-settings/user-video-settings.component.ts b/client/src/app/shared/shared-user-settings/user-video-settings.component.ts
index 4aac60c2b..5d6e11c04 100644
--- a/client/src/app/shared/shared-user-settings/user-video-settings.component.ts
+++ b/client/src/app/shared/shared-user-settings/user-video-settings.component.ts
@@ -80,7 +80,7 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
80 } 80 }
81 81
82 updateDetails (onlyKeys?: string[]) { 82 updateDetails (onlyKeys?: string[]) {
83 const nsfwPolicy = this.form.value[ 'nsfwPolicy' ] 83 const nsfwPolicy = this.form.value['nsfwPolicy']
84 const webTorrentEnabled = this.form.value['webTorrentEnabled'] 84 const webTorrentEnabled = this.form.value['webTorrentEnabled']
85 const autoPlayVideo = this.form.value['autoPlayVideo'] 85 const autoPlayVideo = this.form.value['autoPlayVideo']
86 const autoPlayNextVideo = this.form.value['autoPlayNextVideo'] 86 const autoPlayNextVideo = this.form.value['autoPlayNextVideo']
diff --git a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts
index 666199523..a951134eb 100644
--- a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts
+++ b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts
@@ -6,7 +6,7 @@ import { USER_HANDLE_VALIDATOR } from '../form-validators/user-validators'
6@Component({ 6@Component({
7 selector: 'my-remote-subscribe', 7 selector: 'my-remote-subscribe',
8 templateUrl: './remote-subscribe.component.html', 8 templateUrl: './remote-subscribe.component.html',
9 styleUrls: ['./remote-subscribe.component.scss'] 9 styleUrls: [ './remote-subscribe.component.scss' ]
10}) 10})
11export class RemoteSubscribeComponent extends FormReactive implements OnInit { 11export class RemoteSubscribeComponent extends FormReactive implements OnInit {
12 @Input() uri: string 12 @Input() uri: string
@@ -42,17 +42,21 @@ export class RemoteSubscribeComponent extends FormReactive implements OnInit {
42 // Should not have CORS error because https://tools.ietf.org/html/rfc7033#section-5 42 // Should not have CORS error because https://tools.ietf.org/html/rfc7033#section-5
43 fetch(`${protocol}//${hostname}/.well-known/webfinger?resource=acct:${username}@${hostname}`) 43 fetch(`${protocol}//${hostname}/.well-known/webfinger?resource=acct:${username}@${hostname}`)
44 .then(response => response.json()) 44 .then(response => response.json())
45 .then(data => new Promise((res, rej) => { 45 .then(data => {
46 if (!data || Array.isArray(data.links) === false) return rej() 46 if (!data || Array.isArray(data.links) === false) {
47 throw new Error('Not links in webfinger response')
48 }
47 49
48 const link: { template: string } = data.links.find((link: any) => { 50 const link: { template: string } = data.links.find((link: any) => {
49 return link && typeof link.template === 'string' && link.rel === 'http://ostatus.org/schema/1.0/subscribe' 51 return link && typeof link.template === 'string' && link.rel === 'http://ostatus.org/schema/1.0/subscribe'
50 }) 52 })
51 53
52 if (link && link.template.includes('{uri}')) { 54 if (link?.template.includes('{uri}')) {
53 res(link.template.replace('{uri}', encodeURIComponent(this.uri))) 55 return link.template.replace('{uri}', encodeURIComponent(this.uri))
54 } 56 }
55 })) 57
58 throw new Error('No subscribe template in webfinger response')
59 })
56 .then(window.open) 60 .then(window.open)
57 .catch(err => { 61 .catch(err => {
58 console.error(err) 62 console.error(err)
diff --git a/client/src/app/shared/shared-user-subscription/subscribe-button.component.ts b/client/src/app/shared/shared-user-subscription/subscribe-button.component.ts
index 796493bd9..180bc0565 100644
--- a/client/src/app/shared/shared-user-subscription/subscribe-button.component.ts
+++ b/client/src/app/shared/shared-user-subscription/subscribe-button.component.ts
@@ -137,7 +137,7 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
137 this.notifier.success( 137 this.notifier.success(
138 this.account 138 this.account
139 ? $localize`Unsubscribed from all channels of ${this.account.nameWithHost}` 139 ? $localize`Unsubscribed from all channels of ${this.account.nameWithHost}`
140 : $localize`Unsubscribed from ${this.videoChannels[ 0 ].nameWithHost}`, 140 : $localize`Unsubscribed from ${this.videoChannels[0].nameWithHost}`,
141 141
142 $localize`Unsubscribed` 142 $localize`Unsubscribed`
143 ) 143 )
@@ -157,7 +157,7 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
157 157
158 subscribeStatus (subscribed: boolean) { 158 subscribeStatus (subscribed: boolean) {
159 const accumulator: string[] = [] 159 const accumulator: string[] = []
160 for (const [key, value] of this.subscribed.entries()) { 160 for (const [ key, value ] of this.subscribed.entries()) {
161 if (value === subscribed) accumulator.push(key) 161 if (value === subscribed) accumulator.push(key)
162 } 162 }
163 163
diff --git a/client/src/app/shared/shared-user-subscription/user-subscription.service.ts b/client/src/app/shared/shared-user-subscription/user-subscription.service.ts
index bb44660d2..e4fc09b36 100644
--- a/client/src/app/shared/shared-user-subscription/user-subscription.service.ts
+++ b/client/src/app/shared/shared-user-subscription/user-subscription.service.ts
@@ -46,8 +46,8 @@ export class UserSubscriptionService {
46 } 46 }
47 47
48 getUserSubscriptionVideos (parameters: { 48 getUserSubscriptionVideos (parameters: {
49 videoPagination: ComponentPaginationLight, 49 videoPagination: ComponentPaginationLight
50 sort: VideoSortField, 50 sort: VideoSortField
51 skipCount?: boolean 51 skipCount?: boolean
52 }): Observable<ResultList<Video>> { 52 }): Observable<ResultList<Video>> {
53 const { videoPagination, sort, skipCount } = parameters 53 const { videoPagination, sort, skipCount } = parameters
@@ -131,16 +131,16 @@ export class UserSubscriptionService {
131 131
132 listenToSubscriptionCacheChange (nameWithHost: string) { 132 listenToSubscriptionCacheChange (nameWithHost: string) {
133 if (nameWithHost in this.myAccountSubscriptionCacheObservable) { 133 if (nameWithHost in this.myAccountSubscriptionCacheObservable) {
134 return this.myAccountSubscriptionCacheObservable[ nameWithHost ] 134 return this.myAccountSubscriptionCacheObservable[nameWithHost]
135 } 135 }
136 136
137 const obs = this.existsObservable 137 const obs = this.existsObservable
138 .pipe( 138 .pipe(
139 filter(existsResult => existsResult[ nameWithHost ] !== undefined), 139 filter(existsResult => existsResult[nameWithHost] !== undefined),
140 map(existsResult => existsResult[ nameWithHost ]) 140 map(existsResult => existsResult[nameWithHost])
141 ) 141 )
142 142
143 this.myAccountSubscriptionCacheObservable[ nameWithHost ] = obs 143 this.myAccountSubscriptionCacheObservable[nameWithHost] = obs
144 return obs 144 return obs
145 } 145 }
146 146
@@ -150,16 +150,16 @@ export class UserSubscriptionService {
150 if (nameWithHost in this.myAccountSubscriptionCache) { 150 if (nameWithHost in this.myAccountSubscriptionCache) {
151 logger('Found cache for %d.', nameWithHost) 151 logger('Found cache for %d.', nameWithHost)
152 152
153 return of(this.myAccountSubscriptionCache[ nameWithHost ]) 153 return of(this.myAccountSubscriptionCache[nameWithHost])
154 } 154 }
155 155
156 this.existsSubject.next(nameWithHost) 156 this.existsSubject.next(nameWithHost)
157 157
158 logger('Fetching from network for %d.', nameWithHost) 158 logger('Fetching from network for %d.', nameWithHost)
159 return this.existsObservable.pipe( 159 return this.existsObservable.pipe(
160 filter(existsResult => existsResult[ nameWithHost ] !== undefined), 160 filter(existsResult => existsResult[nameWithHost] !== undefined),
161 map(existsResult => existsResult[ nameWithHost ]), 161 map(existsResult => existsResult[nameWithHost]),
162 tap(result => this.myAccountSubscriptionCache[ nameWithHost ] = result) 162 tap(result => this.myAccountSubscriptionCache[nameWithHost] = result)
163 ) 163 )
164 } 164 }
165 165
diff --git a/client/src/app/shared/shared-video-comment/video-comment.model.ts b/client/src/app/shared/shared-video-comment/video-comment.model.ts
index ba0f57e8f..adab4cfbd 100644
--- a/client/src/app/shared/shared-video-comment/video-comment.model.ts
+++ b/client/src/app/shared/shared-video-comment/video-comment.model.ts
@@ -1,6 +1,10 @@
1import { getAbsoluteAPIUrl } from '@app/helpers' 1import { getAbsoluteAPIUrl } from '@app/helpers'
2import { Account, Actor, Video } from '@app/shared/shared-main' 2import { Actor, Video } from '@app/shared/shared-main'
3import { Account as AccountInterface, VideoComment as VideoCommentServerModel, VideoCommentAdmin as VideoCommentAdminServerModel } from '@shared/models' 3import {
4 Account as AccountInterface,
5 VideoComment as VideoCommentServerModel,
6 VideoCommentAdmin as VideoCommentAdminServerModel
7} from '@shared/models'
4 8
5export class VideoComment implements VideoCommentServerModel { 9export class VideoComment implements VideoCommentServerModel {
6 id: number 10 id: number
diff --git a/client/src/app/shared/shared-video-comment/video-comment.service.ts b/client/src/app/shared/shared-video-comment/video-comment.service.ts
index 4f1452116..5550c96e4 100644
--- a/client/src/app/shared/shared-video-comment/video-comment.service.ts
+++ b/client/src/app/shared/shared-video-comment/video-comment.service.ts
@@ -37,8 +37,8 @@ export class VideoCommentService {
37 37
38 return this.authHttp.post<{ comment: VideoCommentServerModel }>(url, normalizedComment) 38 return this.authHttp.post<{ comment: VideoCommentServerModel }>(url, normalizedComment)
39 .pipe( 39 .pipe(
40 map(data => this.extractVideoComment(data.comment)), 40 map(data => this.extractVideoComment(data.comment)),
41 catchError(err => this.restExtractor.handleError(err)) 41 catchError(err => this.restExtractor.handleError(err))
42 ) 42 )
43 } 43 }
44 44
@@ -54,8 +54,8 @@ export class VideoCommentService {
54 } 54 }
55 55
56 getAdminVideoComments (options: { 56 getAdminVideoComments (options: {
57 pagination: RestPagination, 57 pagination: RestPagination
58 sort: SortMeta, 58 sort: SortMeta
59 search?: string 59 search?: string
60 }): Observable<ResultList<VideoCommentAdmin>> { 60 }): Observable<ResultList<VideoCommentAdmin>> {
61 const { pagination, sort, search } = options 61 const { pagination, sort, search } = options
@@ -75,8 +75,8 @@ export class VideoCommentService {
75 } 75 }
76 76
77 getVideoCommentThreads (parameters: { 77 getVideoCommentThreads (parameters: {
78 videoId: number | string, 78 videoId: number | string
79 componentPagination: ComponentPaginationLight, 79 componentPagination: ComponentPaginationLight
80 sort: string 80 sort: string
81 }): Observable<ThreadsResultList<VideoComment>> { 81 }): Observable<ThreadsResultList<VideoComment>> {
82 const { videoId, componentPagination, sort } = parameters 82 const { videoId, componentPagination, sort } = parameters
@@ -95,7 +95,7 @@ export class VideoCommentService {
95 } 95 }
96 96
97 getVideoThreadComments (parameters: { 97 getVideoThreadComments (parameters: {
98 videoId: number | string, 98 videoId: number | string
99 threadId: number 99 threadId: number
100 }): Observable<VideoCommentThreadTree> { 100 }): Observable<VideoCommentThreadTree> {
101 const { videoId, threadId } = parameters 101 const { videoId, threadId } = parameters
diff --git a/client/src/app/shared/shared-video-miniature/abstract-video-list.ts b/client/src/app/shared/shared-video-miniature/abstract-video-list.ts
index d8b2ee17d..f12ae2ee5 100644
--- a/client/src/app/shared/shared-video-miniature/abstract-video-list.ts
+++ b/client/src/app/shared/shared-video-miniature/abstract-video-list.ts
@@ -42,7 +42,7 @@ enum GroupDate {
42} 42}
43 43
44@Directive() 44@Directive()
45// tslint:disable-next-line: directive-class-suffix 45// eslint-disable-next-line @angular-eslint/directive-class-suffix
46export abstract class AbstractVideoList implements OnInit, OnDestroy, AfterContentInit, DisableForReuseHook { 46export abstract class AbstractVideoList implements OnInit, OnDestroy, AfterContentInit, DisableForReuseHook {
47 @ViewChild('videoListHeader', { static: true, read: ViewContainerRef }) videoListHeader: ViewContainerRef 47 @ViewChild('videoListHeader', { static: true, read: ViewContainerRef }) videoListHeader: ViewContainerRef
48 48
@@ -174,7 +174,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, AfterConte
174 ngAfterContentInit () { 174 ngAfterContentInit () {
175 if (this.videoListHeader) { 175 if (this.videoListHeader) {
176 // some components don't use the header: they use their own template, like my-history.component.html 176 // some components don't use the header: they use their own template, like my-history.component.html
177 this.setHeader.apply(this, [ this.HeaderComponent, this.headerComponentInjector ]) 177 this.setHeader(this.HeaderComponent, this.headerComponentInjector)
178 } 178 }
179 } 179 }
180 180
@@ -278,7 +278,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, AfterConte
278 278
279 if (currentGroupedDate !== period.value) { 279 if (currentGroupedDate !== period.value) {
280 currentGroupedDate = period.value 280 currentGroupedDate = period.value
281 this.groupedDates[ video.id ] = currentGroupedDate 281 this.groupedDates[video.id] = currentGroupedDate
282 } 282 }
283 283
284 break 284 break
@@ -302,13 +302,13 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, AfterConte
302 i: Injector = this.headerComponentInjector 302 i: Injector = this.headerComponentInjector
303 ) { 303 ) {
304 const injector = i || Injector.create({ 304 const injector = i || Injector.create({
305 providers: [{ 305 providers: [ {
306 provide: 'data', 306 provide: 'data',
307 useValue: { 307 useValue: {
308 titlePage: this.titlePage, 308 titlePage: this.titlePage,
309 titleTooltip: this.titleTooltip 309 titleTooltip: this.titleTooltip
310 } 310 }
311 }] 311 } ]
312 }) 312 })
313 const viewContainerRef = this.videoListHeader 313 const viewContainerRef = this.videoListHeader
314 viewContainerRef.clear() 314 viewContainerRef.clear()
@@ -331,9 +331,9 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, AfterConte
331 protected loadPageRouteParams (_queryParams: Params) { /* empty */ } 331 protected loadPageRouteParams (_queryParams: Params) { /* empty */ }
332 332
333 protected loadRouteParams (queryParams: Params) { 333 protected loadRouteParams (queryParams: Params) {
334 this.sort = queryParams[ 'sort' ] as VideoSortField || this.defaultSort 334 this.sort = queryParams['sort'] as VideoSortField || this.defaultSort
335 this.categoryOneOf = queryParams[ 'categoryOneOf' ] 335 this.categoryOneOf = queryParams['categoryOneOf']
336 this.angularState = queryParams[ 'a-state' ] 336 this.angularState = queryParams['a-state']
337 337
338 this.loadPageRouteParams(queryParams) 338 this.loadPageRouteParams(queryParams)
339 } 339 }
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.ts b/client/src/app/shared/shared-video-miniature/video-download.component.ts
index 28fefb9dd..5328f5170 100644
--- a/client/src/app/shared/shared-video-miniature/video-download.component.ts
+++ b/client/src/app/shared/shared-video-miniature/video-download.component.ts
@@ -210,10 +210,10 @@ export class VideoDownloadComponent {
210 210
211 private getMetadataFormat (format: any) { 211 private getMetadataFormat (format: any) {
212 const keyToTranslateFunction = { 212 const keyToTranslateFunction = {
213 'encoder': (value: string) => ({ label: $localize`Encoder`, value }), 213 encoder: (value: string) => ({ label: $localize`Encoder`, value }),
214 'format_long_name': (value: string) => ({ label: $localize`Format name`, value }), 214 format_long_name: (value: string) => ({ label: $localize`Format name`, value }),
215 'size': (value: number) => ({ label: $localize`Size`, value: this.bytesPipe.transform(value, 2) }), 215 size: (value: number) => ({ label: $localize`Size`, value: this.bytesPipe.transform(value, 2) }),
216 'bit_rate': (value: number) => ({ 216 bit_rate: (value: number) => ({
217 label: $localize`Bitrate`, 217 label: $localize`Bitrate`,
218 value: `${this.numbersPipe.transform(value)}bps` 218 value: `${this.numbersPipe.transform(value)}bps`
219 }) 219 })
@@ -234,9 +234,9 @@ export class VideoDownloadComponent {
234 if (!stream) return undefined 234 if (!stream) return undefined
235 235
236 let keyToTranslateFunction = { 236 let keyToTranslateFunction = {
237 'codec_long_name': (value: string) => ({ label: $localize`Codec`, value }), 237 codec_long_name: (value: string) => ({ label: $localize`Codec`, value }),
238 'profile': (value: string) => ({ label: $localize`Profile`, value }), 238 profile: (value: string) => ({ label: $localize`Profile`, value }),
239 'bit_rate': (value: number) => ({ 239 bit_rate: (value: number) => ({
240 label: $localize`Bitrate`, 240 label: $localize`Bitrate`,
241 value: `${this.numbersPipe.transform(value)}bps` 241 value: `${this.numbersPipe.transform(value)}bps`
242 }) 242 })
@@ -244,15 +244,15 @@ export class VideoDownloadComponent {
244 244
245 if (type === 'video') { 245 if (type === 'video') {
246 keyToTranslateFunction = Object.assign(keyToTranslateFunction, { 246 keyToTranslateFunction = Object.assign(keyToTranslateFunction, {
247 'width': (value: number) => ({ label: $localize`Resolution`, value: `${value}x${stream.height}` }), 247 width: (value: number) => ({ label: $localize`Resolution`, value: `${value}x${stream.height}` }),
248 'display_aspect_ratio': (value: string) => ({ label: $localize`Aspect ratio`, value }), 248 display_aspect_ratio: (value: string) => ({ label: $localize`Aspect ratio`, value }),
249 'avg_frame_rate': (value: string) => ({ label: $localize`Average frame rate`, value }), 249 avg_frame_rate: (value: string) => ({ label: $localize`Average frame rate`, value }),
250 'pix_fmt': (value: string) => ({ label: $localize`Pixel format`, value }) 250 pix_fmt: (value: string) => ({ label: $localize`Pixel format`, value })
251 }) 251 })
252 } else { 252 } else {
253 keyToTranslateFunction = Object.assign(keyToTranslateFunction, { 253 keyToTranslateFunction = Object.assign(keyToTranslateFunction, {
254 'sample_rate': (value: number) => ({ label: $localize`Sample rate`, value }), 254 sample_rate: (value: number) => ({ label: $localize`Sample rate`, value }),
255 'channel_layout': (value: number) => ({ label: $localize`Channel Layout`, value }) 255 channel_layout: (value: number) => ({ label: $localize`Channel Layout`, value })
256 }) 256 })
257 } 257 }
258 258
diff --git a/client/src/app/shared/shared-video-miniature/video-list-header.component.ts b/client/src/app/shared/shared-video-miniature/video-list-header.component.ts
index 08a961be1..fed696672 100644
--- a/client/src/app/shared/shared-video-miniature/video-list-header.component.ts
+++ b/client/src/app/shared/shared-video-miniature/video-list-header.component.ts
@@ -11,7 +11,7 @@ export abstract class GenericHeaderComponent {
11 11
12@Component({ 12@Component({
13 selector: 'my-video-list-header', 13 selector: 'my-video-list-header',
14 // tslint:disable-next-line:use-component-view-encapsulation 14 // eslint-disable-next-line @angular-eslint/use-component-view-encapsulation
15 encapsulation: ViewEncapsulation.None, 15 encapsulation: ViewEncapsulation.None,
16 templateUrl: './video-list-header.component.html' 16 templateUrl: './video-list-header.component.html'
17}) 17})
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
index d64ee9b98..456b36926 100644
--- a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
+++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
@@ -108,7 +108,7 @@ export class VideosSelectionComponent extends AbstractVideoList implements OnIni
108 } 108 }
109 109
110 isInSelectionMode () { 110 isInSelectionMode () {
111 return Object.keys(this._selection).some(k => this._selection[ k ] === true) 111 return Object.keys(this._selection).some(k => this._selection[k] === true)
112 } 112 }
113 113
114 generateSyndicationList () { 114 generateSyndicationList () {
diff --git a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
index df3aeddb7..553930595 100644
--- a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
+++ b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
@@ -191,7 +191,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
191 } 191 }
192 192
193 createPlaylist () { 193 createPlaylist () {
194 const displayName = this.form.value[ 'displayName' ] 194 const displayName = this.form.value['displayName']
195 195
196 const videoPlaylistCreate: VideoPlaylistCreate = { 196 const videoPlaylistCreate: VideoPlaylistCreate = {
197 displayName, 197 displayName,
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-element.model.ts b/client/src/app/shared/shared-video-playlist/video-playlist-element.model.ts
index f25f10ede..b661378bd 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist-element.model.ts
+++ b/client/src/app/shared/shared-video-playlist/video-playlist-element.model.ts
@@ -11,7 +11,7 @@ export class VideoPlaylistElement implements ServerVideoPlaylistElement {
11 11
12 video?: Video 12 video?: Video
13 13
14 constructor (hash: ServerVideoPlaylistElement, translations: {}) { 14 constructor (hash: ServerVideoPlaylistElement, translations: { [ id: string ]: string } = {}) {
15 this.id = hash.id 15 this.id = hash.id
16 this.position = hash.position 16 this.position = hash.position
17 this.startTimestamp = hash.startTimestamp 17 this.startTimestamp = hash.startTimestamp
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
index fcc2ce705..6b38d9ca3 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
+++ b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts
@@ -48,7 +48,7 @@ export class VideoPlaylist implements ServerVideoPlaylist {
48 return buildPlaylistWatchPath({ shortUUID: playlist.shortUUID || playlist.uuid }) 48 return buildPlaylistWatchPath({ shortUUID: playlist.shortUUID || playlist.uuid })
49 } 49 }
50 50
51 constructor (hash: ServerVideoPlaylist, translations: {}) { 51 constructor (hash: ServerVideoPlaylist, translations: { [ id: string ]: string }) {
52 const absoluteAPIUrl = getAbsoluteAPIUrl() 52 const absoluteAPIUrl = getAbsoluteAPIUrl()
53 53
54 this.id = hash.id 54 this.id = hash.id
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist.service.ts b/client/src/app/shared/shared-video-playlist/video-playlist.service.ts
index a3f1393ff..0a01af593 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist.service.ts
+++ b/client/src/app/shared/shared-video-playlist/video-playlist.service.ts
@@ -279,18 +279,18 @@ export class VideoPlaylistService {
279 } 279 }
280 280
281 listenToVideoPlaylistChange (videoId: number) { 281 listenToVideoPlaylistChange (videoId: number) {
282 if (this.videoExistsObservableCache[ videoId ]) { 282 if (this.videoExistsObservableCache[videoId]) {
283 return this.videoExistsObservableCache[ videoId ] 283 return this.videoExistsObservableCache[videoId]
284 } 284 }
285 285
286 const obs = this.videoExistsInPlaylistObservable 286 const obs = this.videoExistsInPlaylistObservable
287 .pipe( 287 .pipe(
288 map(existsResult => existsResult[ videoId ]), 288 map(existsResult => existsResult[videoId]),
289 filter(r => !!r), 289 filter(r => !!r),
290 tap(result => this.videoExistsCache[ videoId ] = result) 290 tap(result => this.videoExistsCache[videoId] = result)
291 ) 291 )
292 292
293 this.videoExistsObservableCache[ videoId ] = obs 293 this.videoExistsObservableCache[videoId] = obs
294 return obs 294 return obs
295 } 295 }
296 296