aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app')
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts4
-rw-r--r--client/src/app/+admin/overview/users/user-edit/user-edit.ts4
-rw-r--r--client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts2
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts6
-rw-r--r--client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts2
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.ts8
-rw-r--r--client/src/app/+search/search-filters.component.ts6
-rw-r--r--client/src/app/+stats/video/video-stats.component.ts2
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit-utils.ts4
-rw-r--r--client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts4
-rw-r--r--client/src/app/+videos/video-list/overview/overview.service.ts7
-rw-r--r--client/src/app/core/server/server.service.ts3
-rw-r--r--client/src/app/core/users/user-local-storage.service.ts7
-rw-r--r--client/src/app/core/users/user.model.ts6
-rw-r--r--client/src/app/menu/language-chooser.component.ts7
-rw-r--r--client/src/app/shared/shared-actor-image/actor-avatar.component.ts5
-rw-r--r--client/src/app/shared/shared-custom-markup/dynamic-element.service.ts5
-rw-r--r--client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts3
-rw-r--r--client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts3
-rw-r--r--client/src/app/shared/shared-forms/form-validator.service.ts11
-rw-r--r--client/src/app/shared/shared-icons/global-icon.component.ts2
-rw-r--r--client/src/app/shared/shared-instance/instance.service.ts3
-rw-r--r--client/src/app/shared/shared-main/misc/help.component.ts2
-rw-r--r--client/src/app/shared/shared-main/video/video-edit.model.ts6
-rw-r--r--client/src/app/shared/shared-share-modal/video-share.component.ts3
-rw-r--r--client/src/app/shared/shared-video-miniature/video-download.component.ts27
-rw-r--r--client/src/app/shared/shared-video-miniature/video-filters.model.ts2
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-selection.component.ts3
28 files changed, 84 insertions, 63 deletions
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 30e4aa5d5..2c3b7560d 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
@@ -273,11 +273,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
273 273
274 const defaultValues = { 274 const defaultValues = {
275 transcoding: { 275 transcoding: {
276 resolutions: {} 276 resolutions: {} as { [id: string]: string }
277 }, 277 },
278 live: { 278 live: {
279 transcoding: { 279 transcoding: {
280 resolutions: {} 280 resolutions: {} as { [id: string]: string }
281 } 281 }
282 } 282 }
283 } 283 }
diff --git a/client/src/app/+admin/overview/users/user-edit/user-edit.ts b/client/src/app/+admin/overview/users/user-edit/user-edit.ts
index 5d6c6a91e..9547da2d1 100644
--- a/client/src/app/+admin/overview/users/user-edit/user-edit.ts
+++ b/client/src/app/+admin/overview/users/user-edit/user-edit.ts
@@ -53,8 +53,8 @@ export abstract class UserEdit extends FormReactive implements OnInit {
53 this.serverService.getServerLocale() 53 this.serverService.getServerLocale()
54 .subscribe(translations => { 54 .subscribe(translations => {
55 if (authUser.role.id === UserRole.ADMINISTRATOR) { 55 if (authUser.role.id === UserRole.ADMINISTRATOR) {
56 this.roles = Object.keys(USER_ROLE_LABELS) 56 this.roles = Object.entries(USER_ROLE_LABELS)
57 .map(key => ({ value: key.toString(), label: peertubeTranslate(USER_ROLE_LABELS[key], translations) })) 57 .map(([ key, value ]) => ({ value: key.toString(), label: peertubeTranslate(value, translations) }))
58 return 58 return
59 } 59 }
60 60
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
index 2fdc14d85..3fa1c56dc 100644
--- a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
+++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
@@ -104,7 +104,7 @@ export class PluginListInstalledComponent implements OnInit {
104 } 104 }
105 105
106 isUninstalling (plugin: PeerTubePlugin) { 106 isUninstalling (plugin: PeerTubePlugin) {
107 return !!this.uninstall[this.getPluginKey(plugin)] 107 return !!this.uninstalling[this.getPluginKey(plugin)]
108 } 108 }
109 109
110 isTheme (plugin: PeerTubePlugin) { 110 isTheme (plugin: PeerTubePlugin) {
diff --git a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
index 769ab647a..8faba676e 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
@@ -141,11 +141,11 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
141 } 141 }
142 142
143 private loadNotificationSettings () { 143 private loadNotificationSettings () {
144 for (const key of Object.keys(this.user.notificationSettings)) { 144 for (const key of Object.keys(this.user.notificationSettings) as (keyof UserNotificationSetting)[]) {
145 const value = this.user.notificationSettings[key] 145 const value = this.user.notificationSettings[key]
146 this.emailNotifications[key] = value & UserNotificationSettingValue.EMAIL 146 this.emailNotifications[key] = !!(value & UserNotificationSettingValue.EMAIL)
147 147
148 this.webNotifications[key] = value & UserNotificationSettingValue.WEB 148 this.webNotifications[key] = !!(value & UserNotificationSettingValue.WEB)
149 } 149 }
150 } 150 }
151} 151}
diff --git a/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts b/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts
index 74dbe222d..1f7287f44 100644
--- a/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts
+++ b/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts
@@ -124,7 +124,7 @@ export class MyVideoChannelSyncsComponent extends RestTable implements OnInit {
124 return '/my-library/video-channel-syncs/create' 124 return '/my-library/video-channel-syncs/create'
125 } 125 }
126 126
127 getSyncStateClass (stateId: number) { 127 getSyncStateClass (stateId: VideoChannelSyncState) {
128 return [ 'pt-badge', MyVideoChannelSyncsComponent.STATE_CLASS_BY_ID[stateId] ] 128 return [ 'pt-badge', MyVideoChannelSyncsComponent.STATE_CLASS_BY_ID[stateId] ]
129 } 129 }
130 130
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 46dd304ba..b618b3f88 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
@@ -171,15 +171,15 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
171 .subscribe(result => { 171 .subscribe(result => {
172 this.videosContainedInPlaylists = Object.keys(result).reduce((acc, videoId) => ({ 172 this.videosContainedInPlaylists = Object.keys(result).reduce((acc, videoId) => ({
173 ...acc, 173 ...acc,
174 [videoId]: uniqBy(result[videoId], (p: VideoExistInPlaylist) => p.playlistId) 174 [videoId]: uniqBy(result[+videoId], (p: VideoExistInPlaylist) => p.playlistId)
175 }), this.videosContainedInPlaylists) 175 }), this.videosContainedInPlaylists)
176 }) 176 })
177 } 177 }
178 178
179 async deleteSelectedVideos () { 179 async deleteSelectedVideos () {
180 const toDeleteVideosIds = Object.keys(this.selection) 180 const toDeleteVideosIds = Object.entries(this.selection)
181 .filter(k => this.selection[k] === true) 181 .filter(([ _k, v ]) => v === true)
182 .map(k => parseInt(k, 10)) 182 .map(([ k, _v ]) => parseInt(k, 10))
183 183
184 const res = await this.confirmService.confirm( 184 const res = await this.confirmService.confirm(
185 prepareIcu($localize`Do you really want to delete {length, plural, =1 {this video} other {{length} videos}}?`)( 185 prepareIcu($localize`Do you really want to delete {length, plural, =1 {this video} other {{length} videos}}?`)(
diff --git a/client/src/app/+search/search-filters.component.ts b/client/src/app/+search/search-filters.component.ts
index f9de04706..a6fc51383 100644
--- a/client/src/app/+search/search-filters.component.ts
+++ b/client/src/app/+search/search-filters.component.ts
@@ -118,11 +118,11 @@ export class SearchFiltersComponent implements OnInit {
118 this.onDurationOrPublishedUpdated() 118 this.onDurationOrPublishedUpdated()
119 } 119 }
120 120
121 resetField (fieldName: string, value?: any) { 121 resetField (fieldName: keyof AdvancedSearch, value?: any) {
122 this.advancedSearch[fieldName] = value 122 (this.advancedSearch as any)[fieldName] = value
123 } 123 }
124 124
125 resetLocalField (fieldName: string, value?: any) { 125 resetLocalField (fieldName: keyof SearchFiltersComponent, value?: any) {
126 this[fieldName] = value 126 this[fieldName] = value
127 this.onDurationOrPublishedUpdated() 127 this.onDurationOrPublishedUpdated()
128 } 128 }
diff --git a/client/src/app/+stats/video/video-stats.component.ts b/client/src/app/+stats/video/video-stats.component.ts
index 18312ec33..fa5e33ab6 100644
--- a/client/src/app/+stats/video/video-stats.component.ts
+++ b/client/src/app/+stats/video/video-stats.component.ts
@@ -47,7 +47,7 @@ export class VideoStatsComponent implements OnInit {
47 chartHeight = '300px' 47 chartHeight = '300px'
48 chartWidth: string = null 48 chartWidth: string = null
49 49
50 availableCharts: { id: string, label: string, zoomEnabled: boolean }[] = [] 50 availableCharts: { id: ActiveGraphId, label: string, zoomEnabled: boolean }[] = []
51 activeGraphId: ActiveGraphId = 'viewers' 51 activeGraphId: ActiveGraphId = 'viewers'
52 52
53 video: VideoDetails 53 video: VideoDetails
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 db1ef8d73..214bde680 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
@@ -8,11 +8,11 @@ function hydrateFormFromVideo (formGroup: FormGroup, video: VideoEdit, thumbnail
8 8
9 const objects = [ 9 const objects = [
10 { 10 {
11 url: 'thumbnailUrl', 11 url: 'thumbnailUrl' as 'thumbnailUrl',
12 name: 'thumbnailfile' 12 name: 'thumbnailfile'
13 }, 13 },
14 { 14 {
15 url: 'previewUrl', 15 url: 'previewUrl' as 'previewUrl',
16 name: 'previewfile' 16 name: 'previewfile'
17 } 17 }
18 ] 18 ]
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 28edcfdcb..96bdb28c9 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
@@ -263,8 +263,8 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
263 this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video) 263 this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video)
264 this.loadMoreThreads() 264 this.loadMoreThreads()
265 265
266 if (this.activatedRoute.params['threadId']) { 266 if (this.activatedRoute.snapshot.params['threadId']) {
267 this.processHighlightedThread(+this.activatedRoute.params['threadId']) 267 this.processHighlightedThread(+this.activatedRoute.snapshot.params['threadId'])
268 } 268 }
269 } 269 }
270 } 270 }
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 12d2aa1cb..4a7d9c7c5 100644
--- a/client/src/app/+videos/video-list/overview/overview.service.ts
+++ b/client/src/app/+videos/video-list/overview/overview.service.ts
@@ -5,6 +5,7 @@ import { Injectable } from '@angular/core'
5import { RestExtractor, ServerService } from '@app/core' 5import { RestExtractor, ServerService } from '@app/core'
6import { immutableAssign } from '@app/helpers' 6import { immutableAssign } from '@app/helpers'
7import { VideoService } from '@app/shared/shared-main' 7import { VideoService } from '@app/shared/shared-main'
8import { objectKeysTyped } from '@shared/core-utils'
8import { peertubeTranslate } from '@shared/core-utils/i18n' 9import { peertubeTranslate } from '@shared/core-utils/i18n'
9import { VideosOverview as VideosOverviewServer } from '@shared/models' 10import { VideosOverview as VideosOverviewServer } from '@shared/models'
10import { environment } from '../../../../environments/environment' 11import { environment } from '../../../../environments/environment'
@@ -42,7 +43,7 @@ export class OverviewService {
42 } 43 }
43 44
44 // Build videos objects 45 // Build videos objects
45 for (const key of Object.keys(serverVideosOverview)) { 46 for (const key of objectKeysTyped(serverVideosOverview)) {
46 for (const object of serverVideosOverview[key]) { 47 for (const object of serverVideosOverview[key]) {
47 observables.push( 48 observables.push(
48 of(object.videos) 49 of(object.videos)
@@ -50,7 +51,9 @@ export class OverviewService {
50 switchMap(videos => this.videosService.extractVideos({ total: 0, data: videos })), 51 switchMap(videos => this.videosService.extractVideos({ total: 0, data: videos })),
51 map(result => result.data), 52 map(result => result.data),
52 tap(videos => { 53 tap(videos => {
53 videosOverviewResult[key].push(immutableAssign(object, { videos })) 54 // FIXME: typings & lint
55 // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
56 videosOverviewResult[key].push(immutableAssign(object, { videos }) as any)
54 }) 57 })
55 ) 58 )
56 ) 59 )
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts
index 9f191f0a6..fc269749b 100644
--- a/client/src/app/core/server/server.service.ts
+++ b/client/src/app/core/server/server.service.ts
@@ -193,7 +193,8 @@ export class ServerService {
193 } 193 }
194 194
195 private loadHTMLConfigLocally () { 195 private loadHTMLConfigLocally () {
196 const configString = window['PeerTubeServerConfig'] 196 // FIXME: typings
197 const configString = (window as any)['PeerTubeServerConfig']
197 if (!configString) { 198 if (!configString) {
198 throw new Error('Could not find PeerTubeServerConfig in HTML') 199 throw new Error('Could not find PeerTubeServerConfig in HTML')
199 } 200 }
diff --git a/client/src/app/core/users/user-local-storage.service.ts b/client/src/app/core/users/user-local-storage.service.ts
index 1e629249a..a87f3b98a 100644
--- a/client/src/app/core/users/user-local-storage.service.ts
+++ b/client/src/app/core/users/user-local-storage.service.ts
@@ -4,7 +4,8 @@ import { Injectable } from '@angular/core'
4import { AuthService, AuthStatus } from '@app/core/auth' 4import { AuthService, AuthStatus } from '@app/core/auth'
5import { getBoolOrDefault } from '@root-helpers/local-storage-utils' 5import { getBoolOrDefault } from '@root-helpers/local-storage-utils'
6import { logger } from '@root-helpers/logger' 6import { logger } from '@root-helpers/logger'
7import { UserLocalStorageKeys, OAuthUserTokens } from '@root-helpers/users' 7import { OAuthUserTokens, UserLocalStorageKeys } from '@root-helpers/users'
8import { objectKeysTyped } from '@shared/core-utils'
8import { UserRole, UserUpdateMe } from '@shared/models' 9import { UserRole, UserUpdateMe } from '@shared/models'
9import { NSFWPolicyType } from '@shared/models/videos' 10import { NSFWPolicyType } from '@shared/models/videos'
10import { ServerService } from '../server' 11import { ServerService } from '../server'
@@ -122,7 +123,7 @@ export class UserLocalStorageService {
122 } 123 }
123 124
124 setUserInfo (profile: UserUpdateMe) { 125 setUserInfo (profile: UserUpdateMe) {
125 const localStorageKeys: { [ id in keyof UserUpdateMe ]: string } = { 126 const localStorageKeys = {
126 nsfwPolicy: UserLocalStorageKeys.NSFW_POLICY, 127 nsfwPolicy: UserLocalStorageKeys.NSFW_POLICY,
127 p2pEnabled: UserLocalStorageKeys.P2P_ENABLED, 128 p2pEnabled: UserLocalStorageKeys.P2P_ENABLED,
128 autoPlayVideo: UserLocalStorageKeys.AUTO_PLAY_VIDEO, 129 autoPlayVideo: UserLocalStorageKeys.AUTO_PLAY_VIDEO,
@@ -132,7 +133,7 @@ export class UserLocalStorageService {
132 videoLanguages: UserLocalStorageKeys.VIDEO_LANGUAGES 133 videoLanguages: UserLocalStorageKeys.VIDEO_LANGUAGES
133 } 134 }
134 135
135 const obj = Object.keys(localStorageKeys) 136 const obj: [ string, string | boolean | string[] ][] = objectKeysTyped(localStorageKeys)
136 .filter(key => key in profile) 137 .filter(key => key in profile)
137 .map(key => ([ localStorageKeys[key], profile[key] ])) 138 .map(key => ([ localStorageKeys[key], profile[key] ]))
138 139
diff --git a/client/src/app/core/users/user.model.ts b/client/src/app/core/users/user.model.ts
index 2d783145f..d57608f1c 100644
--- a/client/src/app/core/users/user.model.ts
+++ b/client/src/app/core/users/user.model.ts
@@ -1,4 +1,5 @@
1import { Account } from '@app/shared/shared-main/account/account.model' 1import { Account } from '@app/shared/shared-main/account/account.model'
2import { objectKeysTyped } from '@shared/core-utils'
2import { hasUserRight } from '@shared/core-utils/users' 3import { hasUserRight } from '@shared/core-utils/users'
3import { 4import {
4 ActorImage, 5 ActorImage,
@@ -130,8 +131,9 @@ export class User implements UserServerModel {
130 } 131 }
131 132
132 patch (obj: UserServerModel) { 133 patch (obj: UserServerModel) {
133 for (const key of Object.keys(obj)) { 134 for (const key of objectKeysTyped(obj)) {
134 this[key] = obj[key] 135 // FIXME: typings
136 (this as any)[key] = obj[key]
135 } 137 }
136 138
137 if (obj.account !== undefined) { 139 if (obj.account !== undefined) {
diff --git a/client/src/app/menu/language-chooser.component.ts b/client/src/app/menu/language-chooser.component.ts
index b42e41855..f7ae69717 100644
--- a/client/src/app/menu/language-chooser.component.ts
+++ b/client/src/app/menu/language-chooser.component.ts
@@ -1,6 +1,7 @@
1import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' 1import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core'
2import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers' 2import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
3import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 3import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
4import { objectKeysTyped } from '@shared/core-utils'
4import { getCompleteLocale, getShortLocale, I18N_LOCALES } from '@shared/core-utils/i18n' 5import { getCompleteLocale, getShortLocale, I18N_LOCALES } from '@shared/core-utils/i18n'
5 6
6@Component({ 7@Component({
@@ -17,8 +18,8 @@ export class LanguageChooserComponent {
17 private modalService: NgbModal, 18 private modalService: NgbModal,
18 @Inject(LOCALE_ID) private localeId: string 19 @Inject(LOCALE_ID) private localeId: string
19 ) { 20 ) {
20 const l = Object.keys(I18N_LOCALES) 21 const l = objectKeysTyped(I18N_LOCALES)
21 .map(k => ({ id: k, label: I18N_LOCALES[k], iso: getShortLocale(k) })) 22 .map(k => ({ id: k, label: I18N_LOCALES[k], iso: getShortLocale(k) }))
22 23
23 this.languages = sortBy(l, 'label') 24 this.languages = sortBy(l, 'label')
24 } 25 }
@@ -35,7 +36,7 @@ export class LanguageChooserComponent {
35 const english = 'English' 36 const english = 'English'
36 const locale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId) 37 const locale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId)
37 38
38 if (locale) return I18N_LOCALES[locale] || english 39 if (locale) return I18N_LOCALES[locale as keyof typeof I18N_LOCALES] || english
39 return english 40 return english
40 } 41 }
41} 42}
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 f1c1aa03f..ab2e02ad7 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
@@ -1,6 +1,7 @@
1import { Component, Input, OnChanges, OnInit } from '@angular/core' 1import { Component, Input, OnChanges, OnInit } from '@angular/core'
2import { VideoChannel } from '../shared-main' 2import { VideoChannel } from '../shared-main'
3import { Account } from '../shared-main/account/account.model' 3import { Account } from '../shared-main/account/account.model'
4import { objectKeysTyped } from '@shared/core-utils'
4 5
5type ActorInput = { 6type ActorInput = {
6 name: string 7 name: string
@@ -154,8 +155,8 @@ export class ActorAvatarComponent implements OnInit, OnChanges {
154 'wxyz': 'dark-blue' 155 'wxyz': 'dark-blue'
155 } 156 }
156 157
157 const theme = Object.keys(themes) 158 const theme = objectKeysTyped(themes)
158 .find(chars => chars.includes(initialLowercase)) 159 .find(chars => chars.includes(initialLowercase))
159 160
160 return themes[theme] || 'blue' 161 return themes[theme] || 'blue'
161 } 162 }
diff --git a/client/src/app/shared/shared-custom-markup/dynamic-element.service.ts b/client/src/app/shared/shared-custom-markup/dynamic-element.service.ts
index 208dba721..a12907055 100644
--- a/client/src/app/shared/shared-custom-markup/dynamic-element.service.ts
+++ b/client/src/app/shared/shared-custom-markup/dynamic-element.service.ts
@@ -10,6 +10,7 @@ import {
10 SimpleChanges, 10 SimpleChanges,
11 Type 11 Type
12} from '@angular/core' 12} from '@angular/core'
13import { objectKeysTyped } from '@shared/core-utils'
13 14
14@Injectable() 15@Injectable()
15export class DynamicElementService { 16export class DynamicElementService {
@@ -41,12 +42,12 @@ export class DynamicElementService {
41 setModel <T> (componentRef: ComponentRef<T>, attributes: Partial<T>) { 42 setModel <T> (componentRef: ComponentRef<T>, attributes: Partial<T>) {
42 const changes: SimpleChanges = {} 43 const changes: SimpleChanges = {}
43 44
44 for (const key of Object.keys(attributes)) { 45 for (const key of objectKeysTyped(attributes)) {
45 const previousValue = componentRef.instance[key] 46 const previousValue = componentRef.instance[key]
46 const newValue = attributes[key] 47 const newValue = attributes[key]
47 48
48 componentRef.instance[key] = newValue 49 componentRef.instance[key] = newValue
49 changes[key] = new SimpleChange(previousValue, newValue, previousValue === undefined) 50 changes[key as string] = new SimpleChange(previousValue, newValue, previousValue === undefined)
50 } 51 }
51 52
52 const component = componentRef.instance 53 const component = componentRef.instance
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 21774b7aa..bd93929c9 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
@@ -2,6 +2,7 @@ import { finalize } from 'rxjs/operators'
2import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core' 2import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
3import { AuthService, Notifier } from '@app/core' 3import { AuthService, Notifier } from '@app/core'
4import { FindInBulkService } from '@app/shared/shared-search' 4import { FindInBulkService } from '@app/shared/shared-search'
5import { objectKeysTyped } from '@shared/core-utils'
5import { Video } from '../../shared-main' 6import { Video } from '../../shared-main'
6import { MiniatureDisplayOptions } from '../../shared-video-miniature' 7import { MiniatureDisplayOptions } from '../../shared-video-miniature'
7import { CustomMarkupComponent } from './shared' 8import { CustomMarkupComponent } from './shared'
@@ -47,7 +48,7 @@ export class VideoMiniatureMarkupComponent implements CustomMarkupComponent, OnI
47 48
48 ngOnInit () { 49 ngOnInit () {
49 if (this.onlyDisplayTitle) { 50 if (this.onlyDisplayTitle) {
50 for (const key of Object.keys(this.displayOptions)) { 51 for (const key of objectKeysTyped(this.displayOptions)) {
51 this.displayOptions[key] = false 52 this.displayOptions[key] = false
52 } 53 }
53 } 54 }
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts
index 7c2e7db6a..81363be87 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts
@@ -1,6 +1,7 @@
1import { finalize } from 'rxjs/operators' 1import { finalize } from 'rxjs/operators'
2import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core' 2import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
3import { AuthService, Notifier } from '@app/core' 3import { AuthService, Notifier } from '@app/core'
4import { objectKeysTyped } from '@shared/core-utils'
4import { VideoSortField } from '@shared/models' 5import { VideoSortField } from '@shared/models'
5import { Video, VideoService } from '../../shared-main' 6import { Video, VideoService } from '../../shared-main'
6import { MiniatureDisplayOptions } from '../../shared-video-miniature' 7import { MiniatureDisplayOptions } from '../../shared-video-miniature'
@@ -66,7 +67,7 @@ export class VideosListMarkupComponent implements CustomMarkupComponent, OnInit
66 67
67 ngOnInit () { 68 ngOnInit () {
68 if (this.onlyDisplayTitle) { 69 if (this.onlyDisplayTitle) {
69 for (const key of Object.keys(this.displayOptions)) { 70 for (const key of objectKeysTyped(this.displayOptions)) {
70 this.displayOptions[key] = false 71 this.displayOptions[key] = false
71 } 72 }
72 } 73 }
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 897008242..14ee044b5 100644
--- a/client/src/app/shared/shared-forms/form-validator.service.ts
+++ b/client/src/app/shared/shared-forms/form-validator.service.ts
@@ -1,5 +1,6 @@
1import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { AsyncValidatorFn, FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms' 2import { AsyncValidatorFn, FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms'
3import { objectKeysTyped } from '@shared/core-utils'
3import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' 4import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model'
4import { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive.service' 5import { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive.service'
5 6
@@ -47,13 +48,14 @@ export class FormValidatorService {
47 obj: BuildFormArgument, 48 obj: BuildFormArgument,
48 defaultValues: BuildFormDefaultValues = {} 49 defaultValues: BuildFormDefaultValues = {}
49 ) { 50 ) {
50 for (const name of Object.keys(obj)) { 51 for (const name of objectKeysTyped(obj)) {
51 formErrors[name] = '' 52 formErrors[name] = ''
52 53
53 const field = obj[name] 54 const field = obj[name]
54 if (this.isRecursiveField(field)) { 55 if (this.isRecursiveField(field)) {
55 this.updateFormGroup( 56 this.updateFormGroup(
56 form[name], 57 // FIXME: typings
58 (form as any)[name],
57 formErrors[name] as FormReactiveErrors, 59 formErrors[name] as FormReactiveErrors,
58 validationMessages[name] as FormReactiveValidationMessages, 60 validationMessages[name] as FormReactiveValidationMessages,
59 obj[name] as BuildFormArgument, 61 obj[name] as BuildFormArgument,
@@ -67,7 +69,7 @@ export class FormValidatorService {
67 const defaultValue = defaultValues[name] || '' 69 const defaultValue = defaultValues[name] || ''
68 70
69 form.addControl( 71 form.addControl(
70 name, 72 name + '',
71 new FormControl(defaultValue, field?.VALIDATORS as ValidatorFn[], field?.ASYNC_VALIDATORS as AsyncValidatorFn[]) 73 new FormControl(defaultValue, field?.VALIDATORS as ValidatorFn[], field?.ASYNC_VALIDATORS as AsyncValidatorFn[])
72 ) 74 )
73 } 75 }
@@ -75,7 +77,8 @@ export class FormValidatorService {
75 77
76 updateTreeValidity (group: FormGroup | FormArray): void { 78 updateTreeValidity (group: FormGroup | FormArray): void {
77 for (const key of Object.keys(group.controls)) { 79 for (const key of Object.keys(group.controls)) {
78 const abstractControl = group.controls[key] as FormControl 80 // FIXME: typings
81 const abstractControl = (group.controls as any)[key] as FormControl
79 82
80 if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) { 83 if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) {
81 this.updateTreeValidity(abstractControl) 84 this.updateTreeValidity(abstractControl)
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 96179cbe6..eea460831 100644
--- a/client/src/app/shared/shared-icons/global-icon.component.ts
+++ b/client/src/app/shared/shared-icons/global-icon.component.ts
@@ -112,7 +112,7 @@ export class GlobalIconComponent implements OnInit {
112 } 112 }
113 } 113 }
114 114
115 private getSVGContent (options: { name: string }) { 115 private getSVGContent (options: { name: GlobalIconName }) {
116 return icons[options.name] 116 return icons[options.name]
117 } 117 }
118} 118}
diff --git a/client/src/app/shared/shared-instance/instance.service.ts b/client/src/app/shared/shared-instance/instance.service.ts
index 2defffbbe..3088f0899 100644
--- a/client/src/app/shared/shared-instance/instance.service.ts
+++ b/client/src/app/shared/shared-instance/instance.service.ts
@@ -3,6 +3,7 @@ import { catchError, map } from 'rxjs/operators'
3import { HttpClient } from '@angular/common/http' 3import { HttpClient } from '@angular/common/http'
4import { Injectable } from '@angular/core' 4import { Injectable } from '@angular/core'
5import { MarkdownService, RestExtractor, ServerService } from '@app/core' 5import { MarkdownService, RestExtractor, ServerService } from '@app/core'
6import { objectKeysTyped } from '@shared/core-utils'
6import { peertubeTranslate } from '@shared/core-utils/i18n' 7import { peertubeTranslate } from '@shared/core-utils/i18n'
7import { About } from '@shared/models' 8import { About } from '@shared/models'
8import { environment } from '../../../environments/environment' 9import { environment } from '../../../environments/environment'
@@ -55,7 +56,7 @@ export class InstanceService {
55 hardwareInformation: '' 56 hardwareInformation: ''
56 } 57 }
57 58
58 for (const key of Object.keys(html)) { 59 for (const key of objectKeysTyped(html)) {
59 html[key] = await this.markdownService.enhancedMarkdownToHTML({ markdown: about.instance[key] }) 60 html[key] = await this.markdownService.enhancedMarkdownToHTML({ markdown: about.instance[key] })
60 } 61 }
61 62
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 37e2abd97..80fe0e160 100644
--- a/client/src/app/shared/shared-main/misc/help.component.ts
+++ b/client/src/app/shared/shared-main/misc/help.component.ts
@@ -77,7 +77,7 @@ export class HelpComponent implements OnInit, OnChanges, AfterContentInit {
77 } 77 }
78 78
79 private createMarkdownList (rules: string[]) { 79 private createMarkdownList (rules: string[]) {
80 const rulesToText = { 80 const rulesToText: { [id: string]: string } = {
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`,
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 91d57cb6b..47eee80d8 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
@@ -1,6 +1,7 @@
1import { getAbsoluteAPIUrl } from '@app/helpers' 1import { getAbsoluteAPIUrl } from '@app/helpers'
2import { VideoPrivacy, VideoScheduleUpdate, VideoUpdate } from '@shared/models' 2import { VideoPrivacy, VideoScheduleUpdate, VideoUpdate } from '@shared/models'
3import { VideoDetails } from './video-details.model' 3import { VideoDetails } from './video-details.model'
4import { objectKeysTyped } from '@shared/core-utils'
4 5
5export class VideoEdit implements VideoUpdate { 6export class VideoEdit implements VideoUpdate {
6 static readonly SPECIAL_SCHEDULED_PRIVACY = -1 7 static readonly SPECIAL_SCHEDULED_PRIVACY = -1
@@ -65,8 +66,9 @@ export class VideoEdit implements VideoUpdate {
65 } 66 }
66 67
67 patch (values: { [ id: string ]: any }) { 68 patch (values: { [ id: string ]: any }) {
68 Object.keys(values).forEach((key) => { 69 objectKeysTyped(values).forEach(key => {
69 this[key] = values[key] 70 // FIXME: typings
71 (this as any)[key] = values[key]
70 }) 72 })
71 73
72 // If schedule publication, the video is private and will be changed to public privacy 74 // If schedule publication, the video is private and will be changed to public privacy
diff --git a/client/src/app/shared/shared-share-modal/video-share.component.ts b/client/src/app/shared/shared-share-modal/video-share.component.ts
index 1b69aa2d0..32f900f15 100644
--- a/client/src/app/shared/shared-share-modal/video-share.component.ts
+++ b/client/src/app/shared/shared-share-modal/video-share.component.ts
@@ -106,7 +106,8 @@ export class VideoShareComponent {
106 includeVideoInPlaylist: false 106 includeVideoInPlaylist: false
107 }, { 107 }, {
108 set: (target, prop, value) => { 108 set: (target, prop, value) => {
109 target[prop] = value 109 // FIXME: typings
110 (target as any)[prop] = value
110 111
111 if (prop === 'embedP2P') { 112 if (prop === 'embedP2P') {
112 // Auto enabled warning title if P2P is enabled 113 // Auto enabled warning title if P2P is enabled
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 4135542dc..cac82d8d0 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
@@ -1,4 +1,4 @@
1import { mapValues, pick } from 'lodash-es' 1import { mapValues } from 'lodash-es'
2import { firstValueFrom } from 'rxjs' 2import { firstValueFrom } from 'rxjs'
3import { tap } from 'rxjs/operators' 3import { tap } from 'rxjs/operators'
4import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' 4import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core'
@@ -6,11 +6,12 @@ import { HooksService } from '@app/core'
6import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' 6import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
7import { logger } from '@root-helpers/logger' 7import { logger } from '@root-helpers/logger'
8import { videoRequiresAuth } from '@root-helpers/video' 8import { videoRequiresAuth } from '@root-helpers/video'
9import { objectKeysTyped, pick } from '@shared/core-utils'
9import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' 10import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models'
10import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoFileTokenService, VideoService } from '../shared-main' 11import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoFileTokenService, VideoService } from '../shared-main'
11 12
12type DownloadType = 'video' | 'subtitles' 13type DownloadType = 'video' | 'subtitles'
13type FileMetadata = { [key: string]: { label: string, value: string } } 14type FileMetadata = { [key: string]: { label: string, value: string | number } }
14 15
15@Component({ 16@Component({
16 selector: 'my-video-download', 17 selector: 'my-video-download',
@@ -218,10 +219,10 @@ export class VideoDownloadComponent {
218 const keyToTranslateFunction = { 219 const keyToTranslateFunction = {
219 encoder: (value: string) => ({ label: $localize`Encoder`, value }), 220 encoder: (value: string) => ({ label: $localize`Encoder`, value }),
220 format_long_name: (value: string) => ({ label: $localize`Format name`, value }), 221 format_long_name: (value: string) => ({ label: $localize`Format name`, value }),
221 size: (value: number) => ({ label: $localize`Size`, value: this.bytesPipe.transform(value, 2) }), 222 size: (value: number | string) => ({ label: $localize`Size`, value: this.bytesPipe.transform(+value, 2) }),
222 bit_rate: (value: number) => ({ 223 bit_rate: (value: number | string) => ({
223 label: $localize`Bitrate`, 224 label: $localize`Bitrate`,
224 value: `${this.numbersPipe.transform(value)}bps` 225 value: `${this.numbersPipe.transform(+value)}bps`
225 }) 226 })
226 } 227 }
227 228
@@ -230,8 +231,8 @@ export class VideoDownloadComponent {
230 delete sanitizedFormat.tags 231 delete sanitizedFormat.tags
231 232
232 return mapValues( 233 return mapValues(
233 pick(sanitizedFormat, Object.keys(keyToTranslateFunction)), 234 pick(sanitizedFormat, objectKeysTyped(keyToTranslateFunction)),
234 (val, key) => keyToTranslateFunction[key](val) 235 (val: string, key: keyof typeof keyToTranslateFunction) => keyToTranslateFunction[key](val)
235 ) 236 )
236 } 237 }
237 238
@@ -242,29 +243,29 @@ export class VideoDownloadComponent {
242 let keyToTranslateFunction = { 243 let keyToTranslateFunction = {
243 codec_long_name: (value: string) => ({ label: $localize`Codec`, value }), 244 codec_long_name: (value: string) => ({ label: $localize`Codec`, value }),
244 profile: (value: string) => ({ label: $localize`Profile`, value }), 245 profile: (value: string) => ({ label: $localize`Profile`, value }),
245 bit_rate: (value: number) => ({ 246 bit_rate: (value: number | string) => ({
246 label: $localize`Bitrate`, 247 label: $localize`Bitrate`,
247 value: `${this.numbersPipe.transform(value)}bps` 248 value: `${this.numbersPipe.transform(+value)}bps`
248 }) 249 })
249 } 250 }
250 251
251 if (type === 'video') { 252 if (type === 'video') {
252 keyToTranslateFunction = Object.assign(keyToTranslateFunction, { 253 keyToTranslateFunction = Object.assign(keyToTranslateFunction, {
253 width: (value: number) => ({ label: $localize`Resolution`, value: `${value}x${stream.height}` }), 254 width: (value: string | number) => ({ label: $localize`Resolution`, value: `${value}x${stream.height}` }),
254 display_aspect_ratio: (value: string) => ({ label: $localize`Aspect ratio`, value }), 255 display_aspect_ratio: (value: string) => ({ label: $localize`Aspect ratio`, value }),
255 avg_frame_rate: (value: string) => ({ label: $localize`Average frame rate`, value }), 256 avg_frame_rate: (value: string) => ({ label: $localize`Average frame rate`, value }),
256 pix_fmt: (value: string) => ({ label: $localize`Pixel format`, value }) 257 pix_fmt: (value: string) => ({ label: $localize`Pixel format`, value })
257 }) 258 })
258 } else { 259 } else {
259 keyToTranslateFunction = Object.assign(keyToTranslateFunction, { 260 keyToTranslateFunction = Object.assign(keyToTranslateFunction, {
260 sample_rate: (value: number) => ({ label: $localize`Sample rate`, value }), 261 sample_rate: (value: string | number) => ({ label: $localize`Sample rate`, value }),
261 channel_layout: (value: number) => ({ label: $localize`Channel Layout`, value }) 262 channel_layout: (value: string | number) => ({ label: $localize`Channel Layout`, value })
262 }) 263 })
263 } 264 }
264 265
265 return mapValues( 266 return mapValues(
266 pick(stream, Object.keys(keyToTranslateFunction)), 267 pick(stream, Object.keys(keyToTranslateFunction)),
267 (val, key) => keyToTranslateFunction[key](val) 268 (val: string, key: keyof typeof keyToTranslateFunction) => keyToTranslateFunction[key](val)
268 ) 269 )
269 } 270 }
270 271
diff --git a/client/src/app/shared/shared-video-miniature/video-filters.model.ts b/client/src/app/shared/shared-video-miniature/video-filters.model.ts
index 6b4b72c75..4db73b25a 100644
--- a/client/src/app/shared/shared-video-miniature/video-filters.model.ts
+++ b/client/src/app/shared/shared-video-miniature/video-filters.model.ts
@@ -84,7 +84,7 @@ export class VideoFilters {
84 if (specificKey && specificKey !== key) continue 84 if (specificKey && specificKey !== key) continue
85 85
86 // FIXME: typings 86 // FIXME: typings
87 this[key as any] = value 87 (this as any)[key] = value
88 } 88 }
89 89
90 this.buildActiveFilters() 90 this.buildActiveFilters()
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 460a0080e..86fe502e2 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
@@ -2,6 +2,7 @@ import { Observable, Subject } from 'rxjs'
2import { AfterContentInit, Component, ContentChildren, EventEmitter, Input, Output, QueryList, TemplateRef } from '@angular/core' 2import { AfterContentInit, Component, ContentChildren, EventEmitter, Input, Output, QueryList, TemplateRef } from '@angular/core'
3import { ComponentPagination, Notifier, User } from '@app/core' 3import { ComponentPagination, Notifier, User } from '@app/core'
4import { logger } from '@root-helpers/logger' 4import { logger } from '@root-helpers/logger'
5import { objectKeysTyped } from '@shared/core-utils'
5import { ResultList, VideosExistInPlaylists, VideoSortField } from '@shared/models' 6import { ResultList, VideosExistInPlaylists, VideoSortField } from '@shared/models'
6import { PeerTubeTemplateDirective, Video } from '../shared-main' 7import { PeerTubeTemplateDirective, Video } from '../shared-main'
7import { MiniatureDisplayOptions } from './video-miniature.component' 8import { MiniatureDisplayOptions } from './video-miniature.component'
@@ -93,7 +94,7 @@ export class VideosSelectionComponent implements AfterContentInit {
93 } 94 }
94 95
95 isInSelectionMode () { 96 isInSelectionMode () {
96 return Object.keys(this._selection).some(k => this._selection[k] === true) 97 return objectKeysTyped(this._selection).some(k => this._selection[k] === true)
97 } 98 }
98 99
99 videoById (index: number, video: Video) { 100 videoById (index: number, video: Video) {