diff options
Diffstat (limited to 'client/src/app/shared')
51 files changed, 373 insertions, 164 deletions
diff --git a/client/src/app/shared/form-validators/user-validators.ts b/client/src/app/shared/form-validators/user-validators.ts index 3262853d8..b93de75ea 100644 --- a/client/src/app/shared/form-validators/user-validators.ts +++ b/client/src/app/shared/form-validators/user-validators.ts | |||
@@ -61,6 +61,15 @@ export const USER_EXISTING_PASSWORD_VALIDATOR: BuildFormValidator = { | |||
61 | } | 61 | } |
62 | } | 62 | } |
63 | 63 | ||
64 | export const USER_OTP_TOKEN_VALIDATOR: BuildFormValidator = { | ||
65 | VALIDATORS: [ | ||
66 | Validators.required | ||
67 | ], | ||
68 | MESSAGES: { | ||
69 | required: $localize`OTP token is required.` | ||
70 | } | ||
71 | } | ||
72 | |||
64 | export const USER_PASSWORD_VALIDATOR = { | 73 | export const USER_PASSWORD_VALIDATOR = { |
65 | VALIDATORS: [ | 74 | VALIDATORS: [ |
66 | Validators.required, | 75 | Validators.required, |
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 32d3b0093..569a37b17 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 | |||
@@ -2,7 +2,6 @@ import * as debug from 'debug' | |||
2 | import truncate from 'lodash-es/truncate' | 2 | import truncate from 'lodash-es/truncate' |
3 | import { SortMeta } from 'primeng/api' | 3 | import { SortMeta } from 'primeng/api' |
4 | import { Component, Input, OnInit, ViewChild } from '@angular/core' | 4 | import { Component, Input, OnInit, ViewChild } from '@angular/core' |
5 | import { DomSanitizer } from '@angular/platform-browser' | ||
6 | import { ActivatedRoute, Router } from '@angular/router' | 5 | import { ActivatedRoute, Router } from '@angular/router' |
7 | import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' | 6 | import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' |
8 | import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main' | 7 | import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main' |
@@ -73,8 +72,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit { | |||
73 | private videoService: VideoService, | 72 | private videoService: VideoService, |
74 | private videoBlocklistService: VideoBlockService, | 73 | private videoBlocklistService: VideoBlockService, |
75 | private confirmService: ConfirmService, | 74 | private confirmService: ConfirmService, |
76 | private markdownRenderer: MarkdownService, | 75 | private markdownRenderer: MarkdownService |
77 | private sanitizer: DomSanitizer | ||
78 | ) { | 76 | ) { |
79 | super() | 77 | super() |
80 | } | 78 | } |
@@ -216,8 +214,8 @@ export class AbuseListTableComponent extends RestTable implements OnInit { | |||
216 | abuse.truncatedCommentHtml = abuse.commentHtml = $localize`Deleted comment` | 214 | abuse.truncatedCommentHtml = abuse.commentHtml = $localize`Deleted comment` |
217 | } else { | 215 | } else { |
218 | const truncated = truncate(abuse.comment.text, { length: 100 }) | 216 | const truncated = truncate(abuse.comment.text, { length: 100 }) |
219 | abuse.truncatedCommentHtml = await this.markdownRenderer.textMarkdownToHTML(truncated, true) | 217 | abuse.truncatedCommentHtml = await this.markdownRenderer.textMarkdownToHTML({ markdown: truncated, withHtml: true }) |
220 | abuse.commentHtml = await this.markdownRenderer.textMarkdownToHTML(abuse.comment.text, true) | 218 | abuse.commentHtml = await this.markdownRenderer.textMarkdownToHTML({ markdown: abuse.comment.text, withHtml: true }) |
221 | } | 219 | } |
222 | } | 220 | } |
223 | 221 | ||
@@ -274,7 +272,8 @@ export class AbuseListTableComponent extends RestTable implements OnInit { | |||
274 | }, | 272 | }, |
275 | { | 273 | { |
276 | label: $localize`Delete report`, | 274 | label: $localize`Delete report`, |
277 | handler: abuse => this.isAdminView() && this.removeAbuse(abuse) | 275 | handler: abuse => this.removeAbuse(abuse), |
276 | isDisplayed: () => this.isAdminView() | ||
278 | } | 277 | } |
279 | ] | 278 | ] |
280 | } | 279 | } |
@@ -452,6 +451,6 @@ export class AbuseListTableComponent extends RestTable implements OnInit { | |||
452 | } | 451 | } |
453 | 452 | ||
454 | private toHtml (text: string) { | 453 | private toHtml (text: string) { |
455 | return this.markdownRenderer.textMarkdownToHTML(text) | 454 | return this.markdownRenderer.textMarkdownToHTML({ markdown: text }) |
456 | } | 455 | } |
457 | } | 456 | } |
diff --git a/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts b/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts index d24a5d58d..12d503f56 100644 --- a/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts +++ b/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { AuthService, HtmlRendererService, Notifier } from '@app/core' | 2 | import { AuthService, HtmlRendererService, Notifier } from '@app/core' |
3 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 3 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
4 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 4 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
5 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 5 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
6 | import { logger } from '@root-helpers/logger' | 6 | import { logger } from '@root-helpers/logger' |
@@ -29,7 +29,7 @@ export class AbuseMessageModalComponent extends FormReactive implements OnInit { | |||
29 | private abuse: UserAbuse | 29 | private abuse: UserAbuse |
30 | 30 | ||
31 | constructor ( | 31 | constructor ( |
32 | protected formValidatorService: FormValidatorService, | 32 | protected formReactiveService: FormReactiveService, |
33 | private modalService: NgbModal, | 33 | private modalService: NgbModal, |
34 | private htmlRenderer: HtmlRendererService, | 34 | private htmlRenderer: HtmlRendererService, |
35 | private auth: AuthService, | 35 | private auth: AuthService, |
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 2600da8da..4ad807d25 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 | |||
@@ -1,6 +1,6 @@ | |||
1 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { Notifier } from '@app/core' | 2 | import { Notifier } from '@app/core' |
3 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 3 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
4 | import { AbuseService } from '@app/shared/shared-moderation' | 4 | import { AbuseService } from '@app/shared/shared-moderation' |
5 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 5 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
6 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 6 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
@@ -20,7 +20,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI | |||
20 | private openedModal: NgbModalRef | 20 | private openedModal: NgbModalRef |
21 | 21 | ||
22 | constructor ( | 22 | constructor ( |
23 | protected formValidatorService: FormValidatorService, | 23 | protected formReactiveService: FormReactiveService, |
24 | private modalService: NgbModal, | 24 | private modalService: NgbModal, |
25 | private notifier: Notifier, | 25 | private notifier: Notifier, |
26 | private abuseService: AbuseService | 26 | private abuseService: AbuseService |
diff --git a/client/src/app/shared/shared-custom-markup/custom-markup.service.ts b/client/src/app/shared/shared-custom-markup/custom-markup.service.ts index d738a644e..618c3dd4f 100644 --- a/client/src/app/shared/shared-custom-markup/custom-markup.service.ts +++ b/client/src/app/shared/shared-custom-markup/custom-markup.service.ts | |||
@@ -58,7 +58,7 @@ export class CustomMarkupService { | |||
58 | } | 58 | } |
59 | 59 | ||
60 | async buildElement (text: string) { | 60 | async buildElement (text: string) { |
61 | const html = await this.markdown.customPageMarkdownToHTML(text, this.getSupportedTags()) | 61 | const html = await this.markdown.customPageMarkdownToHTML({ markdown: text, additionalAllowedTags: this.getSupportedTags() }) |
62 | 62 | ||
63 | const rootElement = document.createElement('div') | 63 | const rootElement = document.createElement('div') |
64 | rootElement.innerHTML = html | 64 | rootElement.innerHTML = html |
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 e9c466a90..ba12b7139 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 | |||
@@ -42,7 +42,11 @@ export class ChannelMiniatureMarkupComponent implements CustomMarkupComponent, O | |||
42 | tap(channel => { | 42 | tap(channel => { |
43 | this.channel = channel | 43 | this.channel = channel |
44 | }), | 44 | }), |
45 | switchMap(() => from(this.markdown.textMarkdownToHTML(this.channel.description))), | 45 | switchMap(() => from(this.markdown.textMarkdownToHTML({ |
46 | markdown: this.channel.description, | ||
47 | withEmoji: true, | ||
48 | withHtml: true | ||
49 | }))), | ||
46 | tap(html => { | 50 | tap(html => { |
47 | this.descriptionHTML = html | 51 | this.descriptionHTML = html |
48 | }), | 52 | }), |
diff --git a/client/src/app/shared/shared-forms/form-reactive.service.ts b/client/src/app/shared/shared-forms/form-reactive.service.ts new file mode 100644 index 000000000..f1b7e0ef2 --- /dev/null +++ b/client/src/app/shared/shared-forms/form-reactive.service.ts | |||
@@ -0,0 +1,101 @@ | |||
1 | import { Injectable } from '@angular/core' | ||
2 | import { AbstractControl, FormGroup } from '@angular/forms' | ||
3 | import { wait } from '@root-helpers/utils' | ||
4 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' | ||
5 | import { FormValidatorService } from './form-validator.service' | ||
6 | |||
7 | export type FormReactiveErrors = { [ id: string ]: string | FormReactiveErrors } | ||
8 | export type FormReactiveValidationMessages = { | ||
9 | [ id: string ]: { [ name: string ]: string } | FormReactiveValidationMessages | ||
10 | } | ||
11 | |||
12 | @Injectable() | ||
13 | export class FormReactiveService { | ||
14 | |||
15 | constructor (private formValidatorService: FormValidatorService) { | ||
16 | |||
17 | } | ||
18 | |||
19 | buildForm (obj: BuildFormArgument, defaultValues: BuildFormDefaultValues = {}) { | ||
20 | const { formErrors, validationMessages, form } = this.formValidatorService.buildForm(obj, defaultValues) | ||
21 | |||
22 | form.statusChanges.subscribe(async () => { | ||
23 | // FIXME: remove when https://github.com/angular/angular/issues/41519 is fixed | ||
24 | await this.waitPendingCheck(form) | ||
25 | |||
26 | this.onStatusChanged({ form, formErrors, validationMessages }) | ||
27 | }) | ||
28 | |||
29 | return { form, formErrors, validationMessages } | ||
30 | } | ||
31 | |||
32 | async waitPendingCheck (form: FormGroup) { | ||
33 | if (form.status !== 'PENDING') return | ||
34 | |||
35 | // FIXME: the following line does not work: https://github.com/angular/angular/issues/41519 | ||
36 | // return firstValueFrom(form.statusChanges.pipe(filter(status => status !== 'PENDING'))) | ||
37 | // So we have to fallback to active wait :/ | ||
38 | |||
39 | do { | ||
40 | await wait(10) | ||
41 | } while (form.status === 'PENDING') | ||
42 | } | ||
43 | |||
44 | markAllAsDirty (controlsArg: { [ key: string ]: AbstractControl }) { | ||
45 | const controls = controlsArg | ||
46 | |||
47 | for (const key of Object.keys(controls)) { | ||
48 | const control = controls[key] | ||
49 | |||
50 | if (control instanceof FormGroup) { | ||
51 | this.markAllAsDirty(control.controls) | ||
52 | continue | ||
53 | } | ||
54 | |||
55 | control.markAsDirty() | ||
56 | } | ||
57 | } | ||
58 | |||
59 | forceCheck (form: FormGroup, formErrors: any, validationMessages: FormReactiveValidationMessages) { | ||
60 | this.onStatusChanged({ form, formErrors, validationMessages, onlyDirty: false }) | ||
61 | } | ||
62 | |||
63 | private onStatusChanged (options: { | ||
64 | form: FormGroup | ||
65 | formErrors: FormReactiveErrors | ||
66 | validationMessages: FormReactiveValidationMessages | ||
67 | onlyDirty?: boolean // default true | ||
68 | }) { | ||
69 | const { form, formErrors, validationMessages, onlyDirty = true } = options | ||
70 | |||
71 | for (const field of Object.keys(formErrors)) { | ||
72 | if (formErrors[field] && typeof formErrors[field] === 'object') { | ||
73 | this.onStatusChanged({ | ||
74 | form: form.controls[field] as FormGroup, | ||
75 | formErrors: formErrors[field] as FormReactiveErrors, | ||
76 | validationMessages: validationMessages[field] as FormReactiveValidationMessages, | ||
77 | onlyDirty | ||
78 | }) | ||
79 | |||
80 | continue | ||
81 | } | ||
82 | |||
83 | // clear previous error message (if any) | ||
84 | formErrors[field] = '' | ||
85 | const control = form.get(field) | ||
86 | |||
87 | if (!control || (onlyDirty && !control.dirty) || !control.enabled || !control.errors) continue | ||
88 | |||
89 | const staticMessages = validationMessages[field] | ||
90 | for (const key of Object.keys(control.errors)) { | ||
91 | const formErrorValue = control.errors[key] | ||
92 | |||
93 | // Try to find error message in static validation messages first | ||
94 | // Then check if the validator returns a string that is the error | ||
95 | if (staticMessages[key]) formErrors[field] += staticMessages[key] + ' ' | ||
96 | else if (typeof formErrorValue === 'string') formErrors[field] += control.errors[key] | ||
97 | else throw new Error('Form error value of ' + field + ' is invalid') | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-reactive.ts b/client/src/app/shared/shared-forms/form-reactive.ts index a19ffdd82..d1e7be802 100644 --- a/client/src/app/shared/shared-forms/form-reactive.ts +++ b/client/src/app/shared/shared-forms/form-reactive.ts | |||
@@ -1,16 +1,9 @@ | |||
1 | 1 | import { FormGroup } from '@angular/forms' | |
2 | import { AbstractControl, FormGroup } from '@angular/forms' | ||
3 | import { wait } from '@root-helpers/utils' | ||
4 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' | 2 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' |
5 | import { FormValidatorService } from './form-validator.service' | 3 | import { FormReactiveService, FormReactiveValidationMessages } from './form-reactive.service' |
6 | |||
7 | export type FormReactiveErrors = { [ id: string ]: string | FormReactiveErrors } | ||
8 | export type FormReactiveValidationMessages = { | ||
9 | [ id: string ]: { [ name: string ]: string } | FormReactiveValidationMessages | ||
10 | } | ||
11 | 4 | ||
12 | export abstract class FormReactive { | 5 | export abstract class FormReactive { |
13 | protected abstract formValidatorService: FormValidatorService | 6 | protected abstract formReactiveService: FormReactiveService |
14 | protected formChanged = false | 7 | protected formChanged = false |
15 | 8 | ||
16 | form: FormGroup | 9 | form: FormGroup |
@@ -18,86 +11,22 @@ export abstract class FormReactive { | |||
18 | validationMessages: FormReactiveValidationMessages | 11 | validationMessages: FormReactiveValidationMessages |
19 | 12 | ||
20 | buildForm (obj: BuildFormArgument, defaultValues: BuildFormDefaultValues = {}) { | 13 | buildForm (obj: BuildFormArgument, defaultValues: BuildFormDefaultValues = {}) { |
21 | const { formErrors, validationMessages, form } = this.formValidatorService.buildForm(obj, defaultValues) | 14 | const { formErrors, validationMessages, form } = this.formReactiveService.buildForm(obj, defaultValues) |
22 | 15 | ||
23 | this.form = form | 16 | this.form = form |
24 | this.formErrors = formErrors | 17 | this.formErrors = formErrors |
25 | this.validationMessages = validationMessages | 18 | this.validationMessages = validationMessages |
26 | |||
27 | this.form.statusChanges.subscribe(async () => { | ||
28 | // FIXME: remove when https://github.com/angular/angular/issues/41519 is fixed | ||
29 | await this.waitPendingCheck() | ||
30 | |||
31 | this.onStatusChanged(this.form, this.formErrors, this.validationMessages) | ||
32 | }) | ||
33 | } | 19 | } |
34 | 20 | ||
35 | protected async waitPendingCheck () { | 21 | protected async waitPendingCheck () { |
36 | if (this.form.status !== 'PENDING') return | 22 | return this.formReactiveService.waitPendingCheck(this.form) |
37 | |||
38 | // FIXME: the following line does not work: https://github.com/angular/angular/issues/41519 | ||
39 | // return firstValueFrom(this.form.statusChanges.pipe(filter(status => status !== 'PENDING'))) | ||
40 | // So we have to fallback to active wait :/ | ||
41 | |||
42 | do { | ||
43 | await wait(10) | ||
44 | } while (this.form.status === 'PENDING') | ||
45 | } | 23 | } |
46 | 24 | ||
47 | protected markAllAsDirty (controlsArg?: { [ key: string ]: AbstractControl }) { | 25 | protected markAllAsDirty () { |
48 | const controls = controlsArg || this.form.controls | 26 | return this.formReactiveService.markAllAsDirty(this.form.controls) |
49 | |||
50 | for (const key of Object.keys(controls)) { | ||
51 | const control = controls[key] | ||
52 | |||
53 | if (control instanceof FormGroup) { | ||
54 | this.markAllAsDirty(control.controls) | ||
55 | continue | ||
56 | } | ||
57 | |||
58 | control.markAsDirty() | ||
59 | } | ||
60 | } | 27 | } |
61 | 28 | ||
62 | protected forceCheck () { | 29 | protected forceCheck () { |
63 | this.onStatusChanged(this.form, this.formErrors, this.validationMessages, false) | 30 | return this.formReactiveService.forceCheck(this.form, this.formErrors, this.validationMessages) |
64 | } | ||
65 | |||
66 | private onStatusChanged ( | ||
67 | form: FormGroup, | ||
68 | formErrors: FormReactiveErrors, | ||
69 | validationMessages: FormReactiveValidationMessages, | ||
70 | onlyDirty = true | ||
71 | ) { | ||
72 | for (const field of Object.keys(formErrors)) { | ||
73 | if (formErrors[field] && typeof formErrors[field] === 'object') { | ||
74 | this.onStatusChanged( | ||
75 | form.controls[field] as FormGroup, | ||
76 | formErrors[field] as FormReactiveErrors, | ||
77 | validationMessages[field] as FormReactiveValidationMessages, | ||
78 | onlyDirty | ||
79 | ) | ||
80 | continue | ||
81 | } | ||
82 | |||
83 | // clear previous error message (if any) | ||
84 | formErrors[field] = '' | ||
85 | const control = form.get(field) | ||
86 | |||
87 | if (control.dirty) this.formChanged = true | ||
88 | |||
89 | if (!control || (onlyDirty && !control.dirty) || !control.enabled || !control.errors) continue | ||
90 | |||
91 | const staticMessages = validationMessages[field] | ||
92 | for (const key of Object.keys(control.errors)) { | ||
93 | const formErrorValue = control.errors[key] | ||
94 | |||
95 | // Try to find error message in static validation messages first | ||
96 | // Then check if the validator returns a string that is the error | ||
97 | if (staticMessages[key]) formErrors[field] += staticMessages[key] + ' ' | ||
98 | else if (typeof formErrorValue === 'string') formErrors[field] += control.errors[key] | ||
99 | else throw new Error('Form error value of ' + field + ' is invalid') | ||
100 | } | ||
101 | } | ||
102 | } | 31 | } |
103 | } | 32 | } |
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 f67d5bb33..897008242 100644 --- a/client/src/app/shared/shared-forms/form-validator.service.ts +++ b/client/src/app/shared/shared-forms/form-validator.service.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Injectable } from '@angular/core' | 1 | import { Injectable } from '@angular/core' |
2 | import { AsyncValidatorFn, FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms' | 2 | import { AsyncValidatorFn, FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms' |
3 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' | 3 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' |
4 | import { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive' | 4 | import { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive.service' |
5 | 5 | ||
6 | @Injectable() | 6 | @Injectable() |
7 | export class FormValidatorService { | 7 | export class FormValidatorService { |
diff --git a/client/src/app/shared/shared-forms/index.ts b/client/src/app/shared/shared-forms/index.ts index 495785e7b..bff9862f2 100644 --- a/client/src/app/shared/shared-forms/index.ts +++ b/client/src/app/shared/shared-forms/index.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | export * from './advanced-input-filter.component' | 1 | export * from './advanced-input-filter.component' |
2 | export * from './form-reactive.service' | ||
2 | export * from './form-reactive' | 3 | export * from './form-reactive' |
3 | export * from './form-validator.service' | 4 | export * from './form-validator.service' |
4 | export * from './form-validator.service' | 5 | export * from './form-validator.service' |
diff --git a/client/src/app/shared/shared-forms/input-text.component.ts b/client/src/app/shared/shared-forms/input-text.component.ts index d667ed663..aa4a1cba8 100644 --- a/client/src/app/shared/shared-forms/input-text.component.ts +++ b/client/src/app/shared/shared-forms/input-text.component.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { Component, forwardRef, Input } from '@angular/core' | 1 | import { Component, ElementRef, forwardRef, Input, ViewChild } from '@angular/core' |
2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' | 2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' |
3 | import { Notifier } from '@app/core' | 3 | import { Notifier } from '@app/core' |
4 | 4 | ||
@@ -15,6 +15,8 @@ import { Notifier } from '@app/core' | |||
15 | ] | 15 | ] |
16 | }) | 16 | }) |
17 | export class InputTextComponent implements ControlValueAccessor { | 17 | export class InputTextComponent implements ControlValueAccessor { |
18 | @ViewChild('input') inputElement: ElementRef | ||
19 | |||
18 | @Input() inputId = Math.random().toString(11).slice(2, 8) // id cannot be left empty or undefined | 20 | @Input() inputId = Math.random().toString(11).slice(2, 8) // id cannot be left empty or undefined |
19 | @Input() value = '' | 21 | @Input() value = '' |
20 | @Input() autocomplete = 'off' | 22 | @Input() autocomplete = 'off' |
@@ -65,4 +67,10 @@ export class InputTextComponent implements ControlValueAccessor { | |||
65 | update () { | 67 | update () { |
66 | this.propagateChange(this.value) | 68 | this.propagateChange(this.value) |
67 | } | 69 | } |
70 | |||
71 | focus () { | ||
72 | const el: HTMLElement = this.inputElement.nativeElement | ||
73 | |||
74 | el.focus({ preventScroll: true }) | ||
75 | } | ||
68 | } | 76 | } |
diff --git a/client/src/app/shared/shared-forms/markdown-textarea.component.ts b/client/src/app/shared/shared-forms/markdown-textarea.component.ts index 089991884..e3371f22c 100644 --- a/client/src/app/shared/shared-forms/markdown-textarea.component.ts +++ b/client/src/app/shared/shared-forms/markdown-textarea.component.ts | |||
@@ -144,9 +144,9 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit { | |||
144 | 144 | ||
145 | html = result | 145 | html = result |
146 | } else if (this.markdownType === 'text') { | 146 | } else if (this.markdownType === 'text') { |
147 | html = await this.markdownService.textMarkdownToHTML(text) | 147 | html = await this.markdownService.textMarkdownToHTML({ markdown: text }) |
148 | } else { | 148 | } else { |
149 | html = await this.markdownService.enhancedMarkdownToHTML(text) | 149 | html = await this.markdownService.enhancedMarkdownToHTML({ markdown: text }) |
150 | } | 150 | } |
151 | 151 | ||
152 | if (this.markdownVideo) { | 152 | if (this.markdownVideo) { |
diff --git a/client/src/app/shared/shared-forms/shared-form.module.ts b/client/src/app/shared/shared-forms/shared-form.module.ts index 81f076db6..628affb56 100644 --- a/client/src/app/shared/shared-forms/shared-form.module.ts +++ b/client/src/app/shared/shared-forms/shared-form.module.ts | |||
@@ -1,4 +1,3 @@ | |||
1 | |||
2 | import { InputMaskModule } from 'primeng/inputmask' | 1 | import { InputMaskModule } from 'primeng/inputmask' |
3 | import { NgModule } from '@angular/core' | 2 | import { NgModule } from '@angular/core' |
4 | import { FormsModule, ReactiveFormsModule } from '@angular/forms' | 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms' |
@@ -7,6 +6,7 @@ import { SharedGlobalIconModule } from '../shared-icons' | |||
7 | import { SharedMainModule } from '../shared-main/shared-main.module' | 6 | import { SharedMainModule } from '../shared-main/shared-main.module' |
8 | import { AdvancedInputFilterComponent } from './advanced-input-filter.component' | 7 | import { AdvancedInputFilterComponent } from './advanced-input-filter.component' |
9 | import { DynamicFormFieldComponent } from './dynamic-form-field.component' | 8 | import { DynamicFormFieldComponent } from './dynamic-form-field.component' |
9 | import { FormReactiveService } from './form-reactive.service' | ||
10 | import { FormValidatorService } from './form-validator.service' | 10 | import { FormValidatorService } from './form-validator.service' |
11 | import { InputSwitchComponent } from './input-switch.component' | 11 | import { InputSwitchComponent } from './input-switch.component' |
12 | import { InputTextComponent } from './input-text.component' | 12 | import { InputTextComponent } from './input-text.component' |
@@ -96,7 +96,8 @@ import { TimestampInputComponent } from './timestamp-input.component' | |||
96 | ], | 96 | ], |
97 | 97 | ||
98 | providers: [ | 98 | providers: [ |
99 | FormValidatorService | 99 | FormValidatorService, |
100 | FormReactiveService | ||
100 | ] | 101 | ] |
101 | }) | 102 | }) |
102 | export class SharedFormModule { } | 103 | export class SharedFormModule { } |
diff --git a/client/src/app/shared/shared-instance/instance.service.ts b/client/src/app/shared/shared-instance/instance.service.ts index 0241f56ef..89f47db24 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({ markdown: about.instance[key] }) |
55 | } | 55 | } |
56 | 56 | ||
57 | return html | 57 | return html |
diff --git a/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts b/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts index e4b74f3ad..93b3a93d6 100644 --- a/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts +++ b/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts | |||
@@ -27,13 +27,16 @@ export class AuthInterceptor implements HttpInterceptor { | |||
27 | .pipe( | 27 | .pipe( |
28 | catchError((err: HttpErrorResponse) => { | 28 | catchError((err: HttpErrorResponse) => { |
29 | const error = err.error as PeerTubeProblemDocument | 29 | const error = err.error as PeerTubeProblemDocument |
30 | const isOTPMissingError = this.authService.isOTPMissingError(err) | ||
30 | 31 | ||
31 | if (err.status === HttpStatusCode.UNAUTHORIZED_401 && error && error.code === OAuth2ErrorCode.INVALID_TOKEN) { | 32 | if (!isOTPMissingError) { |
32 | return this.handleTokenExpired(req, next) | 33 | if (err.status === HttpStatusCode.UNAUTHORIZED_401 && error && error.code === OAuth2ErrorCode.INVALID_TOKEN) { |
33 | } | 34 | return this.handleTokenExpired(req, next) |
35 | } | ||
34 | 36 | ||
35 | if (err.status === HttpStatusCode.UNAUTHORIZED_401) { | 37 | if (err.status === HttpStatusCode.UNAUTHORIZED_401) { |
36 | return this.handleNotAuthenticated(err) | 38 | return this.handleNotAuthenticated(err) |
39 | } | ||
37 | } | 40 | } |
38 | 41 | ||
39 | return observableThrowError(() => err) | 42 | return observableThrowError(() => err) |
diff --git a/client/src/app/shared/shared-main/buttons/action-dropdown.component.html b/client/src/app/shared/shared-main/buttons/action-dropdown.component.html index 37cf63fcd..474baafd7 100644 --- a/client/src/app/shared/shared-main/buttons/action-dropdown.component.html +++ b/client/src/app/shared/shared-main/buttons/action-dropdown.component.html | |||
@@ -26,7 +26,7 @@ | |||
26 | 26 | ||
27 | <a | 27 | <a |
28 | *ngIf="action.linkBuilder && !action.isHeader" [ngClass]="{ 'with-icon': !!action.iconName }" | 28 | *ngIf="action.linkBuilder && !action.isHeader" [ngClass]="{ 'with-icon': !!action.iconName }" |
29 | class="dropdown-item" [routerLink]="action.linkBuilder(entry)" [title]="action.title || ''" | 29 | class="dropdown-item" [routerLink]="action.linkBuilder(entry)" [queryParams]="getQueryParams(action, entry)" [title]="action.title || ''" |
30 | > | 30 | > |
31 | <ng-container *ngTemplateOutlet="templateActionLabel; context:{ $implicit: action }"></ng-container> | 31 | <ng-container *ngTemplateOutlet="templateActionLabel; context:{ $implicit: action }"></ng-container> |
32 | </a> | 32 | </a> |
diff --git a/client/src/app/shared/shared-main/buttons/action-dropdown.component.ts b/client/src/app/shared/shared-main/buttons/action-dropdown.component.ts index 749773f8a..e39fbd66d 100644 --- a/client/src/app/shared/shared-main/buttons/action-dropdown.component.ts +++ b/client/src/app/shared/shared-main/buttons/action-dropdown.component.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Component, Input } from '@angular/core' | 1 | import { ChangeDetectionStrategy, Component, Input } from '@angular/core' |
2 | import { Params } from '@angular/router' | ||
2 | import { GlobalIconName } from '@app/shared/shared-icons' | 3 | import { GlobalIconName } from '@app/shared/shared-icons' |
3 | 4 | ||
4 | export type DropdownAction<T> = { | 5 | export type DropdownAction<T> = { |
@@ -7,7 +8,10 @@ export type DropdownAction<T> = { | |||
7 | description?: string | 8 | description?: string |
8 | title?: string | 9 | title?: string |
9 | handler?: (a: T) => any | 10 | handler?: (a: T) => any |
11 | |||
10 | linkBuilder?: (a: T) => (string | number)[] | 12 | linkBuilder?: (a: T) => (string | number)[] |
13 | queryParamsBuilder?: (a: T) => Params | ||
14 | |||
11 | isDisplayed?: (a: T) => boolean | 15 | isDisplayed?: (a: T) => boolean |
12 | 16 | ||
13 | class?: string[] | 17 | class?: string[] |
@@ -21,7 +25,8 @@ export type DropdownDirection = 'horizontal' | 'vertical' | |||
21 | @Component({ | 25 | @Component({ |
22 | selector: 'my-action-dropdown', | 26 | selector: 'my-action-dropdown', |
23 | styleUrls: [ './action-dropdown.component.scss' ], | 27 | styleUrls: [ './action-dropdown.component.scss' ], |
24 | templateUrl: './action-dropdown.component.html' | 28 | templateUrl: './action-dropdown.component.html', |
29 | changeDetection: ChangeDetectionStrategy.OnPush | ||
25 | }) | 30 | }) |
26 | 31 | ||
27 | export class ActionDropdownComponent<T> { | 32 | export class ActionDropdownComponent<T> { |
@@ -44,6 +49,12 @@ export class ActionDropdownComponent<T> { | |||
44 | return [ this.actions as DropdownAction<T>[] ] | 49 | return [ this.actions as DropdownAction<T>[] ] |
45 | } | 50 | } |
46 | 51 | ||
52 | getQueryParams (action: DropdownAction<T>, entry: T) { | ||
53 | if (action.queryParamsBuilder) return action.queryParamsBuilder(entry) | ||
54 | |||
55 | return {} | ||
56 | } | ||
57 | |||
47 | areActionsDisplayed (actions: Array<DropdownAction<T> | DropdownAction<T>[]>, entry: T): boolean { | 58 | areActionsDisplayed (actions: Array<DropdownAction<T> | DropdownAction<T>[]>, entry: T): boolean { |
48 | return actions.some(a => { | 59 | return actions.some(a => { |
49 | if (Array.isArray(a)) return this.areActionsDisplayed(a, entry) | 60 | if (Array.isArray(a)) return this.areActionsDisplayed(a, entry) |
diff --git a/client/src/app/shared/shared-main/buttons/button.component.ts b/client/src/app/shared/shared-main/buttons/button.component.ts index 10d67831f..1761938ee 100644 --- a/client/src/app/shared/shared-main/buttons/button.component.ts +++ b/client/src/app/shared/shared-main/buttons/button.component.ts | |||
@@ -1,10 +1,11 @@ | |||
1 | import { Component, Input, OnChanges } from '@angular/core' | 1 | import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core' |
2 | import { GlobalIconName } from '@app/shared/shared-icons' | 2 | import { GlobalIconName } from '@app/shared/shared-icons' |
3 | 3 | ||
4 | @Component({ | 4 | @Component({ |
5 | selector: 'my-button', | 5 | selector: 'my-button', |
6 | styleUrls: [ './button.component.scss' ], | 6 | styleUrls: [ './button.component.scss' ], |
7 | templateUrl: './button.component.html' | 7 | templateUrl: './button.component.html', |
8 | changeDetection: ChangeDetectionStrategy.OnPush | ||
8 | }) | 9 | }) |
9 | 10 | ||
10 | export class ButtonComponent implements OnChanges { | 11 | export class ButtonComponent implements OnChanges { |
diff --git a/client/src/app/shared/shared-main/shared-main.module.ts b/client/src/app/shared/shared-main/shared-main.module.ts index 04b223cc5..c1523bc50 100644 --- a/client/src/app/shared/shared-main/shared-main.module.ts +++ b/client/src/app/shared/shared-main/shared-main.module.ts | |||
@@ -44,7 +44,15 @@ import { | |||
44 | import { PluginPlaceholderComponent, PluginSelectorDirective } from './plugins' | 44 | import { PluginPlaceholderComponent, PluginSelectorDirective } from './plugins' |
45 | import { ActorRedirectGuard } from './router' | 45 | import { ActorRedirectGuard } from './router' |
46 | import { UserHistoryService, UserNotificationsComponent, UserNotificationService, UserQuotaComponent } from './users' | 46 | import { UserHistoryService, UserNotificationsComponent, UserNotificationService, UserQuotaComponent } from './users' |
47 | import { EmbedComponent, RedundancyService, VideoImportService, VideoOwnershipService, VideoResolver, VideoService } from './video' | 47 | import { |
48 | EmbedComponent, | ||
49 | RedundancyService, | ||
50 | VideoFileTokenService, | ||
51 | VideoImportService, | ||
52 | VideoOwnershipService, | ||
53 | VideoResolver, | ||
54 | VideoService | ||
55 | } from './video' | ||
48 | import { VideoCaptionService } from './video-caption' | 56 | import { VideoCaptionService } from './video-caption' |
49 | import { VideoChannelService } from './video-channel' | 57 | import { VideoChannelService } from './video-channel' |
50 | 58 | ||
@@ -185,6 +193,7 @@ import { VideoChannelService } from './video-channel' | |||
185 | VideoImportService, | 193 | VideoImportService, |
186 | VideoOwnershipService, | 194 | VideoOwnershipService, |
187 | VideoService, | 195 | VideoService, |
196 | VideoFileTokenService, | ||
188 | VideoResolver, | 197 | VideoResolver, |
189 | 198 | ||
190 | VideoCaptionService, | 199 | VideoCaptionService, |
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 5e3985526..08811afec 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 | |||
@@ -2,7 +2,7 @@ import { Observable, ReplaySubject } from 'rxjs' | |||
2 | import { catchError, map, tap } from 'rxjs/operators' | 2 | import { catchError, map, tap } from 'rxjs/operators' |
3 | import { HttpClient, HttpParams } from '@angular/common/http' | 3 | import { HttpClient, HttpParams } from '@angular/common/http' |
4 | import { Injectable } from '@angular/core' | 4 | import { Injectable } from '@angular/core' |
5 | import { ComponentPaginationLight, RestExtractor, RestService } from '@app/core' | 5 | import { ComponentPaginationLight, RestExtractor, RestService, ServerService } from '@app/core' |
6 | import { | 6 | import { |
7 | ActorImage, | 7 | ActorImage, |
8 | ResultList, | 8 | ResultList, |
@@ -25,7 +25,8 @@ export class VideoChannelService { | |||
25 | constructor ( | 25 | constructor ( |
26 | private authHttp: HttpClient, | 26 | private authHttp: HttpClient, |
27 | private restService: RestService, | 27 | private restService: RestService, |
28 | private restExtractor: RestExtractor | 28 | private restExtractor: RestExtractor, |
29 | private serverService: ServerService | ||
29 | ) { } | 30 | ) { } |
30 | 31 | ||
31 | static extractVideoChannels (result: ResultList<VideoChannelServer>) { | 32 | static extractVideoChannels (result: ResultList<VideoChannelServer>) { |
@@ -56,9 +57,11 @@ export class VideoChannelService { | |||
56 | }): Observable<ResultList<VideoChannel>> { | 57 | }): Observable<ResultList<VideoChannel>> { |
57 | const { account, componentPagination, withStats = false, sort, search } = options | 58 | const { account, componentPagination, withStats = false, sort, search } = options |
58 | 59 | ||
60 | const defaultCount = this.serverService.getHTMLConfig().videoChannels.maxPerUser | ||
61 | |||
59 | const pagination = componentPagination | 62 | const pagination = componentPagination |
60 | ? this.restService.componentToRestPagination(componentPagination) | 63 | ? this.restService.componentToRestPagination(componentPagination) |
61 | : { start: 0, count: 20 } | 64 | : { start: 0, count: defaultCount } |
62 | 65 | ||
63 | let params = new HttpParams() | 66 | let params = new HttpParams() |
64 | params = this.restService.addRestGetParams(params, pagination, sort) | 67 | params = this.restService.addRestGetParams(params, pagination, sort) |
diff --git a/client/src/app/shared/shared-main/video/index.ts b/client/src/app/shared/shared-main/video/index.ts index 361601456..a2e47883e 100644 --- a/client/src/app/shared/shared-main/video/index.ts +++ b/client/src/app/shared/shared-main/video/index.ts | |||
@@ -2,6 +2,7 @@ export * from './embed.component' | |||
2 | export * from './redundancy.service' | 2 | export * from './redundancy.service' |
3 | export * from './video-details.model' | 3 | export * from './video-details.model' |
4 | export * from './video-edit.model' | 4 | export * from './video-edit.model' |
5 | export * from './video-file-token.service' | ||
5 | export * from './video-import.service' | 6 | export * from './video-import.service' |
6 | export * from './video-ownership.service' | 7 | export * from './video-ownership.service' |
7 | export * from './video.model' | 8 | export * from './video.model' |
diff --git a/client/src/app/shared/shared-main/video/video-file-token.service.ts b/client/src/app/shared/shared-main/video/video-file-token.service.ts new file mode 100644 index 000000000..791607249 --- /dev/null +++ b/client/src/app/shared/shared-main/video/video-file-token.service.ts | |||
@@ -0,0 +1,33 @@ | |||
1 | import { catchError, map, of, tap } from 'rxjs' | ||
2 | import { HttpClient } from '@angular/common/http' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { RestExtractor } from '@app/core' | ||
5 | import { VideoToken } from '@shared/models' | ||
6 | import { VideoService } from './video.service' | ||
7 | |||
8 | @Injectable() | ||
9 | export class VideoFileTokenService { | ||
10 | |||
11 | private readonly store = new Map<string, { token: string, expires: Date }>() | ||
12 | |||
13 | constructor ( | ||
14 | private authHttp: HttpClient, | ||
15 | private restExtractor: RestExtractor | ||
16 | ) {} | ||
17 | |||
18 | getVideoFileToken (videoUUID: string) { | ||
19 | const existing = this.store.get(videoUUID) | ||
20 | if (existing) return of(existing) | ||
21 | |||
22 | return this.createVideoFileToken(videoUUID) | ||
23 | .pipe(tap(result => this.store.set(videoUUID, { token: result.token, expires: new Date(result.expires) }))) | ||
24 | } | ||
25 | |||
26 | private createVideoFileToken (videoUUID: string) { | ||
27 | return this.authHttp.post<VideoToken>(`${VideoService.BASE_VIDEO_URL}/${videoUUID}/token`, {}) | ||
28 | .pipe( | ||
29 | map(({ files }) => files), | ||
30 | catchError(err => this.restExtractor.handleError(err)) | ||
31 | ) | ||
32 | } | ||
33 | } | ||
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 c9c6b979c..6fdffb394 100644 --- a/client/src/app/shared/shared-main/video/video.model.ts +++ b/client/src/app/shared/shared-main/video/video.model.ts | |||
@@ -34,6 +34,7 @@ export class Video implements VideoServerModel { | |||
34 | language: VideoConstant<string> | 34 | language: VideoConstant<string> |
35 | privacy: VideoConstant<VideoPrivacy> | 35 | privacy: VideoConstant<VideoPrivacy> |
36 | 36 | ||
37 | truncatedDescription: string | ||
37 | description: string | 38 | description: string |
38 | 39 | ||
39 | duration: number | 40 | duration: number |
@@ -134,6 +135,8 @@ export class Video implements VideoServerModel { | |||
134 | this.privacy = hash.privacy | 135 | this.privacy = hash.privacy |
135 | this.waitTranscoding = hash.waitTranscoding | 136 | this.waitTranscoding = hash.waitTranscoding |
136 | this.state = hash.state | 137 | this.state = hash.state |
138 | |||
139 | this.truncatedDescription = hash.truncatedDescription | ||
137 | this.description = hash.description | 140 | this.description = hash.description |
138 | 141 | ||
139 | this.isLive = hash.isLive | 142 | this.isLive = hash.isLive |
diff --git a/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts b/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts index 20be728f6..ec2fea528 100644 --- a/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts +++ b/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 2 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
3 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 3 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
4 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 4 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
5 | import { splitAndGetNotEmpty, UNIQUE_HOSTS_VALIDATOR } from '../form-validators/host-validators' | 5 | import { splitAndGetNotEmpty, UNIQUE_HOSTS_VALIDATOR } from '../form-validators/host-validators' |
@@ -18,7 +18,7 @@ export class BatchDomainsModalComponent extends FormReactive implements OnInit { | |||
18 | private openedModal: NgbModalRef | 18 | private openedModal: NgbModalRef |
19 | 19 | ||
20 | constructor ( | 20 | constructor ( |
21 | protected formValidatorService: FormValidatorService, | 21 | protected formReactiveService: FormReactiveService, |
22 | private modalService: NgbModal | 22 | private modalService: NgbModal |
23 | ) { | 23 | ) { |
24 | super() | 24 | super() |
diff --git a/client/src/app/shared/shared-moderation/report-modals/account-report.component.ts b/client/src/app/shared/shared-moderation/report-modals/account-report.component.ts index 78c9b3382..d587a9709 100644 --- a/client/src/app/shared/shared-moderation/report-modals/account-report.component.ts +++ b/client/src/app/shared/shared-moderation/report-modals/account-report.component.ts | |||
@@ -2,7 +2,7 @@ import { mapValues, pickBy } from 'lodash-es' | |||
2 | import { Component, OnInit, ViewChild } from '@angular/core' | 2 | import { Component, OnInit, ViewChild } from '@angular/core' |
3 | import { Notifier } from '@app/core' | 3 | import { Notifier } from '@app/core' |
4 | import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-validators' | 4 | import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-validators' |
5 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 5 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
6 | import { Account } from '@app/shared/shared-main' | 6 | import { Account } from '@app/shared/shared-main' |
7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
@@ -26,7 +26,7 @@ export class AccountReportComponent extends FormReactive implements OnInit { | |||
26 | private openedModal: NgbModalRef | 26 | private openedModal: NgbModalRef |
27 | 27 | ||
28 | constructor ( | 28 | constructor ( |
29 | protected formValidatorService: FormValidatorService, | 29 | protected formReactiveService: FormReactiveService, |
30 | private modalService: NgbModal, | 30 | private modalService: NgbModal, |
31 | private abuseService: AbuseService, | 31 | private abuseService: AbuseService, |
32 | private notifier: Notifier | 32 | private notifier: Notifier |
diff --git a/client/src/app/shared/shared-moderation/report-modals/comment-report.component.ts b/client/src/app/shared/shared-moderation/report-modals/comment-report.component.ts index 7c0907ce4..e35d70c8f 100644 --- a/client/src/app/shared/shared-moderation/report-modals/comment-report.component.ts +++ b/client/src/app/shared/shared-moderation/report-modals/comment-report.component.ts | |||
@@ -2,7 +2,7 @@ import { mapValues, pickBy } from 'lodash-es' | |||
2 | import { Component, Input, OnInit, ViewChild } from '@angular/core' | 2 | import { Component, Input, OnInit, ViewChild } from '@angular/core' |
3 | import { Notifier } from '@app/core' | 3 | import { Notifier } from '@app/core' |
4 | import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-validators' | 4 | import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-validators' |
5 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 5 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
6 | import { VideoComment } from '@app/shared/shared-video-comment' | 6 | import { VideoComment } from '@app/shared/shared-video-comment' |
7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
@@ -27,7 +27,7 @@ export class CommentReportComponent extends FormReactive implements OnInit { | |||
27 | private openedModal: NgbModalRef | 27 | private openedModal: NgbModalRef |
28 | 28 | ||
29 | constructor ( | 29 | constructor ( |
30 | protected formValidatorService: FormValidatorService, | 30 | protected formReactiveService: FormReactiveService, |
31 | private modalService: NgbModal, | 31 | private modalService: NgbModal, |
32 | private abuseService: AbuseService, | 32 | private abuseService: AbuseService, |
33 | private notifier: Notifier | 33 | private notifier: Notifier |
diff --git a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts index 38dd92910..16be8e0a1 100644 --- a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts +++ b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts | |||
@@ -3,7 +3,7 @@ import { Component, Input, OnInit, ViewChild } from '@angular/core' | |||
3 | import { DomSanitizer } from '@angular/platform-browser' | 3 | import { DomSanitizer } from '@angular/platform-browser' |
4 | import { Notifier } from '@app/core' | 4 | import { Notifier } from '@app/core' |
5 | import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-validators' | 5 | import { ABUSE_REASON_VALIDATOR } from '@app/shared/form-validators/abuse-validators' |
6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 7 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 8 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
9 | import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' | 9 | import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' |
@@ -27,7 +27,7 @@ export class VideoReportComponent extends FormReactive implements OnInit { | |||
27 | private openedModal: NgbModalRef | 27 | private openedModal: NgbModalRef |
28 | 28 | ||
29 | constructor ( | 29 | constructor ( |
30 | protected formValidatorService: FormValidatorService, | 30 | protected formReactiveService: FormReactiveService, |
31 | private modalService: NgbModal, | 31 | private modalService: NgbModal, |
32 | private abuseService: AbuseService, | 32 | private abuseService: AbuseService, |
33 | private notifier: Notifier, | 33 | private notifier: Notifier, |
diff --git a/client/src/app/shared/shared-moderation/user-ban-modal.component.ts b/client/src/app/shared/shared-moderation/user-ban-modal.component.ts index 617408f2a..27dcf043a 100644 --- a/client/src/app/shared/shared-moderation/user-ban-modal.component.ts +++ b/client/src/app/shared/shared-moderation/user-ban-modal.component.ts | |||
@@ -2,7 +2,7 @@ import { forkJoin } from 'rxjs' | |||
2 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' | 2 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' |
3 | import { Notifier } from '@app/core' | 3 | import { Notifier } from '@app/core' |
4 | import { prepareIcu } from '@app/helpers' | 4 | import { prepareIcu } from '@app/helpers' |
5 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 5 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
6 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 6 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
7 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 7 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
8 | import { User } from '@shared/models' | 8 | import { User } from '@shared/models' |
@@ -25,7 +25,7 @@ export class UserBanModalComponent extends FormReactive implements OnInit { | |||
25 | modalMessage = '' | 25 | modalMessage = '' |
26 | 26 | ||
27 | constructor ( | 27 | constructor ( |
28 | protected formValidatorService: FormValidatorService, | 28 | protected formReactiveService: FormReactiveService, |
29 | private modalService: NgbModal, | 29 | private modalService: NgbModal, |
30 | private notifier: Notifier, | 30 | private notifier: Notifier, |
31 | private userAdminService: UserAdminService, | 31 | private userAdminService: UserAdminService, |
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 f8b22a3f6..3ff53443a 100644 --- a/client/src/app/shared/shared-moderation/video-block.component.ts +++ b/client/src/app/shared/shared-moderation/video-block.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { Notifier } from '@app/core' | 2 | import { Notifier } from '@app/core' |
3 | import { prepareIcu } from '@app/helpers' | 3 | import { prepareIcu } from '@app/helpers' |
4 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 4 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
5 | import { Video } from '@app/shared/shared-main' | 5 | import { Video } from '@app/shared/shared-main' |
6 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 6 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
7 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | 7 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' |
@@ -25,7 +25,7 @@ export class VideoBlockComponent extends FormReactive implements OnInit { | |||
25 | private openedModal: NgbModalRef | 25 | private openedModal: NgbModalRef |
26 | 26 | ||
27 | constructor ( | 27 | constructor ( |
28 | protected formValidatorService: FormValidatorService, | 28 | protected formReactiveService: FormReactiveService, |
29 | private modalService: NgbModal, | 29 | private modalService: NgbModal, |
30 | private videoBlocklistService: VideoBlockService, | 30 | private videoBlocklistService: VideoBlockService, |
31 | private notifier: Notifier | 31 | private notifier: Notifier |
diff --git a/client/src/app/shared/shared-search/find-in-bulk.service.ts b/client/src/app/shared/shared-search/find-in-bulk.service.ts index d2f8c3213..d6ee04379 100644 --- a/client/src/app/shared/shared-search/find-in-bulk.service.ts +++ b/client/src/app/shared/shared-search/find-in-bulk.service.ts | |||
@@ -80,13 +80,18 @@ export class FindInBulkService { | |||
80 | map(result => result.response.data), | 80 | map(result => result.response.data), |
81 | map(data => data.find(finder)) | 81 | map(data => data.find(finder)) |
82 | ) | 82 | ) |
83 | .subscribe(result => { | 83 | .subscribe({ |
84 | if (!result) { | 84 | next: result => { |
85 | obs.error(new Error($localize`Element ${param} not found`)) | 85 | if (!result) { |
86 | } else { | 86 | obs.error(new Error($localize`Element ${param} not found`)) |
87 | return | ||
88 | } | ||
89 | |||
87 | obs.next(result) | 90 | obs.next(result) |
88 | obs.complete() | 91 | obs.complete() |
89 | } | 92 | }, |
93 | |||
94 | error: err => obs.error(err) | ||
90 | }) | 95 | }) |
91 | 96 | ||
92 | observableObject.notifier.next(param) | 97 | observableObject.notifier.next(param) |
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 08e997f7b..f330228e1 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 | |||
@@ -27,7 +27,7 @@ export class SupportModalComponent { | |||
27 | 27 | ||
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({ markdown: support }) |
31 | .then(r => { | 31 | .then(r => { |
32 | this.htmlSupport = r | 32 | this.htmlSupport = r |
33 | }) | 33 | }) |
diff --git a/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts b/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts index 13e2e5424..c2c30d38b 100644 --- a/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts +++ b/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Subject, Subscription } from 'rxjs' | 1 | import { Subject, Subscription } from 'rxjs' |
2 | import { Component, Input, OnDestroy, OnInit } from '@angular/core' | 2 | import { Component, Input, OnDestroy, OnInit } from '@angular/core' |
3 | import { AuthService, Notifier, ServerService, ThemeService, UserService } from '@app/core' | 3 | import { AuthService, Notifier, ServerService, ThemeService, UserService } from '@app/core' |
4 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 4 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
5 | import { HTMLServerConfig, User, UserUpdateMe } from '@shared/models' | 5 | import { HTMLServerConfig, User, UserUpdateMe } from '@shared/models' |
6 | import { SelectOptionsItem } from 'src/types' | 6 | import { SelectOptionsItem } from 'src/types' |
7 | 7 | ||
@@ -22,7 +22,7 @@ export class UserInterfaceSettingsComponent extends FormReactive implements OnIn | |||
22 | private serverConfig: HTMLServerConfig | 22 | private serverConfig: HTMLServerConfig |
23 | 23 | ||
24 | constructor ( | 24 | constructor ( |
25 | protected formValidatorService: FormValidatorService, | 25 | protected formReactiveService: FormReactiveService, |
26 | private authService: AuthService, | 26 | private authService: AuthService, |
27 | private notifier: Notifier, | 27 | private notifier: Notifier, |
28 | private userService: UserService, | 28 | private userService: UserService, |
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 7d6b69469..af0870f12 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 | |||
@@ -3,7 +3,7 @@ import { Subject, Subscription } from 'rxjs' | |||
3 | import { first } from 'rxjs/operators' | 3 | import { first } from 'rxjs/operators' |
4 | import { Component, Input, OnDestroy, OnInit } from '@angular/core' | 4 | import { Component, Input, OnDestroy, OnInit } from '@angular/core' |
5 | import { AuthService, Notifier, ServerService, User, UserService } from '@app/core' | 5 | import { AuthService, Notifier, ServerService, User, UserService } from '@app/core' |
6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
7 | import { UserUpdateMe } from '@shared/models' | 7 | import { UserUpdateMe } from '@shared/models' |
8 | import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type' | 8 | import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type' |
9 | 9 | ||
@@ -22,7 +22,7 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit, | |||
22 | formValuesWatcher: Subscription | 22 | formValuesWatcher: Subscription |
23 | 23 | ||
24 | constructor ( | 24 | constructor ( |
25 | protected formValidatorService: FormValidatorService, | 25 | protected formReactiveService: FormReactiveService, |
26 | private authService: AuthService, | 26 | private authService: AuthService, |
27 | private notifier: Notifier, | 27 | private notifier: Notifier, |
28 | private userService: UserService, | 28 | private userService: UserService, |
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 7bcfdd8aa..61bcd5345 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 | |||
@@ -1,6 +1,6 @@ | |||
1 | import { Component, Input, OnInit } from '@angular/core' | 1 | import { Component, Input, OnInit } from '@angular/core' |
2 | import { Notifier } from '@app/core' | 2 | import { Notifier } from '@app/core' |
3 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 3 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
4 | import { logger } from '@root-helpers/logger' | 4 | import { logger } from '@root-helpers/logger' |
5 | import { USER_HANDLE_VALIDATOR } from '../form-validators/user-validators' | 5 | import { USER_HANDLE_VALIDATOR } from '../form-validators/user-validators' |
6 | 6 | ||
@@ -15,7 +15,7 @@ export class RemoteSubscribeComponent extends FormReactive implements OnInit { | |||
15 | @Input() showHelp = false | 15 | @Input() showHelp = false |
16 | 16 | ||
17 | constructor ( | 17 | constructor ( |
18 | protected formValidatorService: FormValidatorService, | 18 | protected formReactiveService: FormReactiveService, |
19 | private notifier: Notifier | 19 | private notifier: Notifier |
20 | ) { | 20 | ) { |
21 | super() | 21 | super() |
diff --git a/client/src/app/shared/shared-user-subscription/subscribe-button.component.html b/client/src/app/shared/shared-user-subscription/subscribe-button.component.html index 0e09c2697..341b83a04 100644 --- a/client/src/app/shared/shared-user-subscription/subscribe-button.component.html +++ b/client/src/app/shared/shared-user-subscription/subscribe-button.component.html | |||
@@ -37,7 +37,7 @@ | |||
37 | class="btn-group" ngbDropdown autoClose="outside" placement="bottom-right bottom-left bottom auto" | 37 | class="btn-group" ngbDropdown autoClose="outside" placement="bottom-right bottom-left bottom auto" |
38 | role="group" aria-label="Multiple ways to subscribe to the current channel" i18n-aria-label | 38 | role="group" aria-label="Multiple ways to subscribe to the current channel" i18n-aria-label |
39 | > | 39 | > |
40 | <button class="btn dropdown-toggle-split" ngbDropdownToggle aria-label="Open subscription dropdown" i18n-aria-label> | 40 | <button class="btn dropdown-toggle-split last-in-group" ngbDropdownToggle aria-label="Open subscription dropdown" i18n-aria-label> |
41 | <ng-container | 41 | <ng-container |
42 | *ngIf="!isUserLoggedIn(); then userLoggedOut"> | 42 | *ngIf="!isUserLoggedIn(); then userLoggedOut"> |
43 | </ng-container> | 43 | </ng-container> |
diff --git a/client/src/app/shared/shared-users/index.ts b/client/src/app/shared/shared-users/index.ts index 8f90f2515..20e60486d 100644 --- a/client/src/app/shared/shared-users/index.ts +++ b/client/src/app/shared/shared-users/index.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | export * from './user-admin.service' | 1 | export * from './user-admin.service' |
2 | export * from './user-signup.service' | 2 | export * from './user-signup.service' |
3 | export * from './two-factor.service' | ||
3 | 4 | ||
4 | export * from './shared-users.module' | 5 | export * from './shared-users.module' |
diff --git a/client/src/app/shared/shared-users/shared-users.module.ts b/client/src/app/shared/shared-users/shared-users.module.ts index 2a1dadf20..5a1675dc9 100644 --- a/client/src/app/shared/shared-users/shared-users.module.ts +++ b/client/src/app/shared/shared-users/shared-users.module.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | 1 | ||
2 | import { NgModule } from '@angular/core' | 2 | import { NgModule } from '@angular/core' |
3 | import { SharedMainModule } from '../shared-main/shared-main.module' | 3 | import { SharedMainModule } from '../shared-main/shared-main.module' |
4 | import { TwoFactorService } from './two-factor.service' | ||
4 | import { UserAdminService } from './user-admin.service' | 5 | import { UserAdminService } from './user-admin.service' |
5 | import { UserSignupService } from './user-signup.service' | 6 | import { UserSignupService } from './user-signup.service' |
6 | 7 | ||
@@ -15,7 +16,8 @@ import { UserSignupService } from './user-signup.service' | |||
15 | 16 | ||
16 | providers: [ | 17 | providers: [ |
17 | UserSignupService, | 18 | UserSignupService, |
18 | UserAdminService | 19 | UserAdminService, |
20 | TwoFactorService | ||
19 | ] | 21 | ] |
20 | }) | 22 | }) |
21 | export class SharedUsersModule { } | 23 | export class SharedUsersModule { } |
diff --git a/client/src/app/shared/shared-users/two-factor.service.ts b/client/src/app/shared/shared-users/two-factor.service.ts new file mode 100644 index 000000000..9ff916f15 --- /dev/null +++ b/client/src/app/shared/shared-users/two-factor.service.ts | |||
@@ -0,0 +1,52 @@ | |||
1 | import { catchError } from 'rxjs/operators' | ||
2 | import { HttpClient } from '@angular/common/http' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { RestExtractor, UserService } from '@app/core' | ||
5 | import { TwoFactorEnableResult } from '@shared/models' | ||
6 | |||
7 | @Injectable() | ||
8 | export class TwoFactorService { | ||
9 | constructor ( | ||
10 | private authHttp: HttpClient, | ||
11 | private restExtractor: RestExtractor | ||
12 | ) { } | ||
13 | |||
14 | // --------------------------------------------------------------------------- | ||
15 | |||
16 | requestTwoFactor (options: { | ||
17 | userId: number | ||
18 | currentPassword: string | ||
19 | }) { | ||
20 | const { userId, currentPassword } = options | ||
21 | |||
22 | const url = UserService.BASE_USERS_URL + userId + '/two-factor/request' | ||
23 | |||
24 | return this.authHttp.post<TwoFactorEnableResult>(url, { currentPassword }) | ||
25 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
26 | } | ||
27 | |||
28 | confirmTwoFactorRequest (options: { | ||
29 | userId: number | ||
30 | requestToken: string | ||
31 | otpToken: string | ||
32 | }) { | ||
33 | const { userId, requestToken, otpToken } = options | ||
34 | |||
35 | const url = UserService.BASE_USERS_URL + userId + '/two-factor/confirm-request' | ||
36 | |||
37 | return this.authHttp.post(url, { requestToken, otpToken }) | ||
38 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
39 | } | ||
40 | |||
41 | disableTwoFactor (options: { | ||
42 | userId: number | ||
43 | currentPassword?: string | ||
44 | }) { | ||
45 | const { userId, currentPassword } = options | ||
46 | |||
47 | const url = UserService.BASE_USERS_URL + userId + '/two-factor/disable' | ||
48 | |||
49 | return this.authHttp.post(url, { currentPassword }) | ||
50 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
51 | } | ||
52 | } | ||
diff --git a/client/src/app/shared/shared-users/user-admin.service.ts b/client/src/app/shared/shared-users/user-admin.service.ts index 4128358dc..0b04023a3 100644 --- a/client/src/app/shared/shared-users/user-admin.service.ts +++ b/client/src/app/shared/shared-users/user-admin.service.ts | |||
@@ -125,7 +125,10 @@ export class UserAdminService { | |||
125 | } | 125 | } |
126 | 126 | ||
127 | return Object.assign(user, { | 127 | return Object.assign(user, { |
128 | roleLabel: roleLabels[user.role], | 128 | role: { |
129 | id: user.role.id, | ||
130 | label: roleLabels[user.role.id] | ||
131 | }, | ||
129 | videoQuota, | 132 | videoQuota, |
130 | videoQuotaUsed, | 133 | videoQuotaUsed, |
131 | rawVideoQuota: user.videoQuota, | 134 | rawVideoQuota: user.videoQuota, |
diff --git a/client/src/app/shared/shared-video-live/live-stream-information.component.html b/client/src/app/shared/shared-video-live/live-stream-information.component.html index cf30c1ce1..8e61bdbb3 100644 --- a/client/src/app/shared/shared-video-live/live-stream-information.component.html +++ b/client/src/app/shared/shared-video-live/live-stream-information.component.html | |||
@@ -32,7 +32,7 @@ | |||
32 | <div class="form-group-description" i18n>⚠️ Never share your stream key with anyone.</div> | 32 | <div class="form-group-description" i18n>⚠️ Never share your stream key with anyone.</div> |
33 | </div> | 33 | </div> |
34 | 34 | ||
35 | <div class="journal"> | 35 | <div class="journal" *ngIf="latestLiveSessions.length !== 0"> |
36 | <label i18n>Latest live sessions</label> | 36 | <label i18n>Latest live sessions</label> |
37 | 37 | ||
38 | <div class="journal-session" *ngFor="let session of latestLiveSessions"> | 38 | <div class="journal-session" *ngFor="let session of latestLiveSessions"> |
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.html b/client/src/app/shared/shared-video-miniature/video-download.component.html index 1c7458b4b..1f622933d 100644 --- a/client/src/app/shared/shared-video-miniature/video-download.component.html +++ b/client/src/app/shared/shared-video-miniature/video-download.component.html | |||
@@ -48,10 +48,7 @@ | |||
48 | 48 | ||
49 | <ng-template ngbNavContent> | 49 | <ng-template ngbNavContent> |
50 | <div class="nav-content"> | 50 | <div class="nav-content"> |
51 | <my-input-text | 51 | <my-input-text [show]="true" [readonly]="true" [withCopy]="true" [withToggle]="false" [value]="getLink()"></my-input-text> |
52 | *ngIf="!isConfidentialVideo()" | ||
53 | [show]="true" [readonly]="true" [withCopy]="true" [withToggle]="false" [value]="getLink()" | ||
54 | ></my-input-text> | ||
55 | </div> | 52 | </div> |
56 | </ng-template> | 53 | </ng-template> |
57 | </ng-container> | 54 | </ng-container> |
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 47482caaa..667cb107f 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 | |||
@@ -2,11 +2,12 @@ import { mapValues, pick } from 'lodash-es' | |||
2 | import { firstValueFrom } from 'rxjs' | 2 | import { firstValueFrom } from 'rxjs' |
3 | import { tap } from 'rxjs/operators' | 3 | import { tap } from 'rxjs/operators' |
4 | import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' | 4 | import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' |
5 | import { AuthService, HooksService, Notifier } from '@app/core' | 5 | import { HooksService } from '@app/core' |
6 | import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' | 6 | import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' |
7 | import { logger } from '@root-helpers/logger' | 7 | import { logger } from '@root-helpers/logger' |
8 | import { videoRequiresAuth } from '@root-helpers/video' | ||
8 | import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' | 9 | import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' |
9 | import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoService } from '../shared-main' | 10 | import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoFileTokenService, VideoService } from '../shared-main' |
10 | 11 | ||
11 | type DownloadType = 'video' | 'subtitles' | 12 | type DownloadType = 'video' | 'subtitles' |
12 | type FileMetadata = { [key: string]: { label: string, value: string }} | 13 | type FileMetadata = { [key: string]: { label: string, value: string }} |
@@ -32,6 +33,8 @@ export class VideoDownloadComponent { | |||
32 | 33 | ||
33 | type: DownloadType = 'video' | 34 | type: DownloadType = 'video' |
34 | 35 | ||
36 | videoFileToken: string | ||
37 | |||
35 | private activeModal: NgbModalRef | 38 | private activeModal: NgbModalRef |
36 | 39 | ||
37 | private bytesPipe: BytesPipe | 40 | private bytesPipe: BytesPipe |
@@ -42,10 +45,9 @@ export class VideoDownloadComponent { | |||
42 | 45 | ||
43 | constructor ( | 46 | constructor ( |
44 | @Inject(LOCALE_ID) private localeId: string, | 47 | @Inject(LOCALE_ID) private localeId: string, |
45 | private notifier: Notifier, | ||
46 | private modalService: NgbModal, | 48 | private modalService: NgbModal, |
47 | private videoService: VideoService, | 49 | private videoService: VideoService, |
48 | private auth: AuthService, | 50 | private videoFileTokenService: VideoFileTokenService, |
49 | private hooks: HooksService | 51 | private hooks: HooksService |
50 | ) { | 52 | ) { |
51 | this.bytesPipe = new BytesPipe() | 53 | this.bytesPipe = new BytesPipe() |
@@ -71,6 +73,8 @@ export class VideoDownloadComponent { | |||
71 | } | 73 | } |
72 | 74 | ||
73 | show (video: VideoDetails, videoCaptions?: VideoCaption[]) { | 75 | show (video: VideoDetails, videoCaptions?: VideoCaption[]) { |
76 | this.videoFileToken = undefined | ||
77 | |||
74 | this.video = video | 78 | this.video = video |
75 | this.videoCaptions = videoCaptions | 79 | this.videoCaptions = videoCaptions |
76 | 80 | ||
@@ -84,6 +88,11 @@ export class VideoDownloadComponent { | |||
84 | this.subtitleLanguageId = this.videoCaptions[0].language.id | 88 | this.subtitleLanguageId = this.videoCaptions[0].language.id |
85 | } | 89 | } |
86 | 90 | ||
91 | if (videoRequiresAuth(this.video)) { | ||
92 | this.videoFileTokenService.getVideoFileToken(this.video.uuid) | ||
93 | .subscribe(({ token }) => this.videoFileToken = token) | ||
94 | } | ||
95 | |||
87 | this.activeModal.shown.subscribe(() => { | 96 | this.activeModal.shown.subscribe(() => { |
88 | this.hooks.runAction('action:modal.video-download.shown', 'common') | 97 | this.hooks.runAction('action:modal.video-download.shown', 'common') |
89 | }) | 98 | }) |
@@ -155,7 +164,7 @@ export class VideoDownloadComponent { | |||
155 | if (!file) return '' | 164 | if (!file) return '' |
156 | 165 | ||
157 | const suffix = this.isConfidentialVideo() | 166 | const suffix = this.isConfidentialVideo() |
158 | ? '?access_token=' + this.auth.getAccessToken() | 167 | ? '?videoFileToken=' + this.videoFileToken |
159 | : '' | 168 | : '' |
160 | 169 | ||
161 | switch (this.downloadType) { | 170 | switch (this.downloadType) { |
diff --git a/client/src/app/shared/shared-video-miniature/video-filters-header.component.html b/client/src/app/shared/shared-video-miniature/video-filters-header.component.html index 9ddfd7dda..1e92e1952 100644 --- a/client/src/app/shared/shared-video-miniature/video-filters-header.component.html +++ b/client/src/app/shared/shared-video-miniature/video-filters-header.component.html | |||
@@ -47,6 +47,7 @@ | |||
47 | <ng-option i18n value="-publishedAt">Sort by <strong>"Recently Added"</strong></ng-option> | 47 | <ng-option i18n value="-publishedAt">Sort by <strong>"Recently Added"</strong></ng-option> |
48 | <ng-option i18n value="-originallyPublishedAt">Sort by <strong>"Original Publication Date"</strong></ng-option> | 48 | <ng-option i18n value="-originallyPublishedAt">Sort by <strong>"Original Publication Date"</strong></ng-option> |
49 | 49 | ||
50 | <ng-option i18n value="name">Sort by <strong>"Name"</strong></ng-option> | ||
50 | <ng-option i18n *ngIf="isTrendingSortEnabled('most-viewed')" value="-trending">Sort by <strong>"Recent Views"</strong></ng-option> | 51 | <ng-option i18n *ngIf="isTrendingSortEnabled('most-viewed')" value="-trending">Sort by <strong>"Recent Views"</strong></ng-option> |
51 | <ng-option i18n *ngIf="isTrendingSortEnabled('hot')" value="-hot">Sort by <strong>"Hot"</strong></ng-option> | 52 | <ng-option i18n *ngIf="isTrendingSortEnabled('hot')" value="-hot">Sort by <strong>"Hot"</strong></ng-option> |
52 | <ng-option i18n *ngIf="isTrendingSortEnabled('most-liked')" value="-likes">Sort by <strong>"Likes"</strong></ng-option> | 53 | <ng-option i18n *ngIf="isTrendingSortEnabled('most-liked')" value="-likes">Sort by <strong>"Likes"</strong></ng-option> |
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.html b/client/src/app/shared/shared-video-miniature/video-miniature.component.html index e8d2ca1c4..6fdf24b2d 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.html +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.html | |||
@@ -52,6 +52,12 @@ | |||
52 | <ng-container *ngIf="displayOptions.privacyText && displayOptions.state && getStateLabel(video)"> - </ng-container> | 52 | <ng-container *ngIf="displayOptions.privacyText && displayOptions.state && getStateLabel(video)"> - </ng-container> |
53 | <ng-container *ngIf="displayOptions.state">{{ getStateLabel(video) }}</ng-container> | 53 | <ng-container *ngIf="displayOptions.state">{{ getStateLabel(video) }}</ng-container> |
54 | </div> | 54 | </div> |
55 | |||
56 | <div *ngIf="containedInPlaylists" class="video-contained-in-playlists"> | ||
57 | <a *ngFor="let playlist of containedInPlaylists" class="chip rectangular bg-secondary text-light" [routerLink]="['/w/p/', playlist.playlistShortUUID]"> | ||
58 | {{ playlist.playlistDisplayName }} | ||
59 | </a> | ||
60 | </div> | ||
55 | </div> | 61 | </div> |
56 | </div> | 62 | </div> |
57 | 63 | ||
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.scss b/client/src/app/shared/shared-video-miniature/video-miniature.component.scss index a397efdca..ba2adfc5a 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.scss +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.scss | |||
@@ -4,6 +4,10 @@ | |||
4 | 4 | ||
5 | $more-button-width: 40px; | 5 | $more-button-width: 40px; |
6 | 6 | ||
7 | .chip { | ||
8 | @include chip; | ||
9 | } | ||
10 | |||
7 | .video-miniature { | 11 | .video-miniature { |
8 | font-size: 14px; | 12 | font-size: 14px; |
9 | } | 13 | } |
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts index 534a78b3f..85c63c173 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts | |||
@@ -11,7 +11,7 @@ import { | |||
11 | Output | 11 | Output |
12 | } from '@angular/core' | 12 | } from '@angular/core' |
13 | import { AuthService, ScreenService, ServerService, User } from '@app/core' | 13 | import { AuthService, ScreenService, ServerService, User } from '@app/core' |
14 | import { HTMLServerConfig, VideoPlaylistType, VideoPrivacy, VideoState } from '@shared/models' | 14 | import { HTMLServerConfig, VideoExistInPlaylist, VideoPlaylistType, VideoPrivacy, VideoState } from '@shared/models' |
15 | import { LinkType } from '../../../types/link.type' | 15 | import { LinkType } from '../../../types/link.type' |
16 | import { ActorAvatarSize } from '../shared-actor-image/actor-avatar.component' | 16 | import { ActorAvatarSize } from '../shared-actor-image/actor-avatar.component' |
17 | import { Video } from '../shared-main' | 17 | import { Video } from '../shared-main' |
@@ -21,13 +21,15 @@ import { VideoActionsDisplayType } from './video-actions-dropdown.component' | |||
21 | export type MiniatureDisplayOptions = { | 21 | export type MiniatureDisplayOptions = { |
22 | date?: boolean | 22 | date?: boolean |
23 | views?: boolean | 23 | views?: boolean |
24 | by?: boolean | ||
25 | avatar?: boolean | 24 | avatar?: boolean |
26 | privacyLabel?: boolean | 25 | privacyLabel?: boolean |
27 | privacyText?: boolean | 26 | privacyText?: boolean |
28 | state?: boolean | 27 | state?: boolean |
29 | blacklistInfo?: boolean | 28 | blacklistInfo?: boolean |
30 | nsfw?: boolean | 29 | nsfw?: boolean |
30 | |||
31 | by?: boolean | ||
32 | forceChannelInBy?: boolean | ||
31 | } | 33 | } |
32 | @Component({ | 34 | @Component({ |
33 | selector: 'my-video-miniature', | 35 | selector: 'my-video-miniature', |
@@ -38,6 +40,7 @@ export type MiniatureDisplayOptions = { | |||
38 | export class VideoMiniatureComponent implements OnInit { | 40 | export class VideoMiniatureComponent implements OnInit { |
39 | @Input() user: User | 41 | @Input() user: User |
40 | @Input() video: Video | 42 | @Input() video: Video |
43 | @Input() containedInPlaylists: VideoExistInPlaylist[] | ||
41 | 44 | ||
42 | @Input() displayOptions: MiniatureDisplayOptions = { | 45 | @Input() displayOptions: MiniatureDisplayOptions = { |
43 | date: true, | 46 | date: true, |
@@ -47,7 +50,8 @@ export class VideoMiniatureComponent implements OnInit { | |||
47 | privacyLabel: false, | 50 | privacyLabel: false, |
48 | privacyText: false, | 51 | privacyText: false, |
49 | state: false, | 52 | state: false, |
50 | blacklistInfo: false | 53 | blacklistInfo: false, |
54 | forceChannelInBy: false | ||
51 | } | 55 | } |
52 | 56 | ||
53 | @Input() displayVideoActions = true | 57 | @Input() displayVideoActions = true |
@@ -267,6 +271,11 @@ export class VideoMiniatureComponent implements OnInit { | |||
267 | } | 271 | } |
268 | 272 | ||
269 | private setUpBy () { | 273 | private setUpBy () { |
274 | if (this.displayOptions.forceChannelInBy) { | ||
275 | this.ownerDisplayType = 'videoChannel' | ||
276 | return | ||
277 | } | ||
278 | |||
270 | const accountName = this.video.account.name | 279 | const accountName = this.video.account.name |
271 | 280 | ||
272 | // If the video channel name is an UUID (not really displayable, we changed this behaviour in v1.0.0-beta.12) | 281 | // If the video channel name is an UUID (not really displayable, we changed this behaviour in v1.0.0-beta.12) |
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.html b/client/src/app/shared/shared-video-miniature/videos-selection.component.html index 6ea2661e4..6c6db4b96 100644 --- a/client/src/app/shared/shared-video-miniature/videos-selection.component.html +++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.html | |||
@@ -12,6 +12,7 @@ | |||
12 | </div> | 12 | </div> |
13 | 13 | ||
14 | <my-video-miniature | 14 | <my-video-miniature |
15 | [containedInPlaylists]="videosContainedInPlaylists ? videosContainedInPlaylists[video.id] : undefined" | ||
15 | [video]="video" [displayAsRow]="true" [displayOptions]="miniatureDisplayOptions" | 16 | [video]="video" [displayAsRow]="true" [displayOptions]="miniatureDisplayOptions" |
16 | [displayVideoActions]="false" [user]="user" | 17 | [displayVideoActions]="false" [user]="user" |
17 | ></my-video-miniature> | 18 | ></my-video-miniature> |
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 fa3c79bbb..460a0080e 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,7 +2,7 @@ import { Observable, Subject } from 'rxjs' | |||
2 | import { AfterContentInit, Component, ContentChildren, EventEmitter, Input, Output, QueryList, TemplateRef } from '@angular/core' | 2 | import { AfterContentInit, Component, ContentChildren, EventEmitter, Input, Output, QueryList, TemplateRef } from '@angular/core' |
3 | import { ComponentPagination, Notifier, User } from '@app/core' | 3 | import { ComponentPagination, Notifier, User } from '@app/core' |
4 | import { logger } from '@root-helpers/logger' | 4 | import { logger } from '@root-helpers/logger' |
5 | import { ResultList, VideoSortField } from '@shared/models' | 5 | import { ResultList, VideosExistInPlaylists, VideoSortField } from '@shared/models' |
6 | import { PeerTubeTemplateDirective, Video } from '../shared-main' | 6 | import { PeerTubeTemplateDirective, Video } from '../shared-main' |
7 | import { MiniatureDisplayOptions } from './video-miniature.component' | 7 | import { MiniatureDisplayOptions } from './video-miniature.component' |
8 | 8 | ||
@@ -14,6 +14,7 @@ export type SelectionType = { [ id: number ]: boolean } | |||
14 | styleUrls: [ './videos-selection.component.scss' ] | 14 | styleUrls: [ './videos-selection.component.scss' ] |
15 | }) | 15 | }) |
16 | export class VideosSelectionComponent implements AfterContentInit { | 16 | export class VideosSelectionComponent implements AfterContentInit { |
17 | @Input() videosContainedInPlaylists: VideosExistInPlaylists | ||
17 | @Input() user: User | 18 | @Input() user: User |
18 | @Input() pagination: ComponentPagination | 19 | @Input() pagination: ComponentPagination |
19 | 20 | ||
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 e019fdd26..2fc39fc75 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 | |||
@@ -3,11 +3,11 @@ import { Subject, Subscription } from 'rxjs' | |||
3 | import { debounceTime, filter } from 'rxjs/operators' | 3 | import { debounceTime, filter } from 'rxjs/operators' |
4 | import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core' | 4 | import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core' |
5 | import { AuthService, DisableForReuseHook, Notifier } from '@app/core' | 5 | import { AuthService, DisableForReuseHook, Notifier } from '@app/core' |
6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
7 | import { secondsToTime } from '@shared/core-utils' | 7 | import { secondsToTime } from '@shared/core-utils' |
8 | import { | 8 | import { |
9 | CachedVideoExistInPlaylist, | ||
9 | Video, | 10 | Video, |
10 | VideoExistInPlaylist, | ||
11 | VideoPlaylistCreate, | 11 | VideoPlaylistCreate, |
12 | VideoPlaylistElementCreate, | 12 | VideoPlaylistElementCreate, |
13 | VideoPlaylistElementUpdate, | 13 | VideoPlaylistElementUpdate, |
@@ -59,7 +59,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
59 | private pendingAddId: number | 59 | private pendingAddId: number |
60 | 60 | ||
61 | constructor ( | 61 | constructor ( |
62 | protected formValidatorService: FormValidatorService, | 62 | protected formReactiveService: FormReactiveService, |
63 | private authService: AuthService, | 63 | private authService: AuthService, |
64 | private notifier: Notifier, | 64 | private notifier: Notifier, |
65 | private videoPlaylistService: VideoPlaylistService, | 65 | private videoPlaylistService: VideoPlaylistService, |
@@ -330,7 +330,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
330 | } | 330 | } |
331 | } | 331 | } |
332 | 332 | ||
333 | private rebuildPlaylists (existResult: VideoExistInPlaylist[]) { | 333 | private rebuildPlaylists (existResult: CachedVideoExistInPlaylist[]) { |
334 | debugLogger('Got existing results for %d.', this.video.id, existResult) | 334 | debugLogger('Got existing results for %d.', this.video.id, existResult) |
335 | 335 | ||
336 | const oldPlaylists = this.videoPlaylists | 336 | const oldPlaylists = this.videoPlaylists |
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.ts b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.ts index dd9fe0a5a..225c4eb64 100644 --- a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.ts +++ b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.ts | |||
@@ -32,7 +32,7 @@ export class VideoPlaylistMiniatureComponent implements OnInit { | |||
32 | async ngOnInit () { | 32 | async ngOnInit () { |
33 | this.buildPlaylistUrl() | 33 | this.buildPlaylistUrl() |
34 | if (this.displayDescription) { | 34 | if (this.displayDescription) { |
35 | this.playlistDescription = await this.markdownService.textMarkdownToHTML(this.playlist.description) | 35 | this.playlistDescription = await this.markdownService.textMarkdownToHTML({ markdown: this.playlist.description }) |
36 | } | 36 | } |
37 | } | 37 | } |
38 | 38 | ||
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 d71f8f72e..330a51f91 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 | |||
@@ -8,6 +8,8 @@ import { buildBulkObservable, objectToFormData } from '@app/helpers' | |||
8 | import { Account, AccountService, VideoChannel, VideoChannelService } from '@app/shared/shared-main' | 8 | import { Account, AccountService, VideoChannel, VideoChannelService } from '@app/shared/shared-main' |
9 | import { NGX_LOADING_BAR_IGNORED } from '@ngx-loading-bar/http-client' | 9 | import { NGX_LOADING_BAR_IGNORED } from '@ngx-loading-bar/http-client' |
10 | import { | 10 | import { |
11 | CachedVideoExistInPlaylist, | ||
12 | CachedVideosExistInPlaylists, | ||
11 | ResultList, | 13 | ResultList, |
12 | VideoExistInPlaylist, | 14 | VideoExistInPlaylist, |
13 | VideoPlaylist as VideoPlaylistServerModel, | 15 | VideoPlaylist as VideoPlaylistServerModel, |
@@ -34,11 +36,11 @@ export class VideoPlaylistService { | |||
34 | 36 | ||
35 | // Use a replay subject because we "next" a value before subscribing | 37 | // Use a replay subject because we "next" a value before subscribing |
36 | private videoExistsInPlaylistNotifier = new ReplaySubject<number>(1) | 38 | private videoExistsInPlaylistNotifier = new ReplaySubject<number>(1) |
37 | private videoExistsInPlaylistCacheSubject = new Subject<VideosExistInPlaylists>() | 39 | private videoExistsInPlaylistCacheSubject = new Subject<CachedVideosExistInPlaylists>() |
38 | private readonly videoExistsInPlaylistObservable: Observable<VideosExistInPlaylists> | 40 | private readonly videoExistsInPlaylistObservable: Observable<CachedVideosExistInPlaylists> |
39 | 41 | ||
40 | private videoExistsObservableCache: { [ id: number ]: Observable<VideoExistInPlaylist[]> } = {} | 42 | private videoExistsObservableCache: { [ id: number ]: Observable<CachedVideoExistInPlaylist[]> } = {} |
41 | private videoExistsCache: { [ id: number ]: VideoExistInPlaylist[] } = {} | 43 | private videoExistsCache: { [ id: number ]: CachedVideoExistInPlaylist[] } = {} |
42 | 44 | ||
43 | private myAccountPlaylistCache: ResultList<CachedPlaylist> = undefined | 45 | private myAccountPlaylistCache: ResultList<CachedPlaylist> = undefined |
44 | private myAccountPlaylistCacheRunning: Observable<ResultList<CachedPlaylist>> | 46 | private myAccountPlaylistCacheRunning: Observable<ResultList<CachedPlaylist>> |
@@ -346,7 +348,7 @@ export class VideoPlaylistService { | |||
346 | ) | 348 | ) |
347 | } | 349 | } |
348 | 350 | ||
349 | private doVideosExistInPlaylist (videoIds: number[]): Observable<VideosExistInPlaylists> { | 351 | doVideosExistInPlaylist (videoIds: number[]): Observable<VideosExistInPlaylists> { |
350 | const url = VideoPlaylistService.MY_VIDEO_PLAYLIST_URL + 'videos-exist' | 352 | const url = VideoPlaylistService.MY_VIDEO_PLAYLIST_URL + 'videos-exist' |
351 | 353 | ||
352 | let params = new HttpParams() | 354 | let params = new HttpParams() |