Execute Angular migrations

* inject() Function
 * signal inputs
 * outputs
 * signal queries
 * cleanup unused imports

https://angular.dev/reference/migrations
This commit is contained in:
Chocobozzz 2025-02-19 09:58:38 +01:00
parent 8ae98c5ad5
commit 017795cf45
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
456 changed files with 6033 additions and 6590 deletions

View File

@ -38,8 +38,8 @@
"forStatement.spaceAfterForKeyword": true, "forStatement.spaceAfterForKeyword": true,
"forStatement.spaceAfterSemiColons": true, "forStatement.spaceAfterSemiColons": true,
"functionDeclaration.spaceBeforeParentheses": true, "functionDeclaration.spaceBeforeParentheses": true,
"functionExpression.spaceBeforeParentheses": false, "functionExpression.spaceBeforeParentheses": true,
"functionExpression.spaceAfterFunctionKeyword": false, "functionExpression.spaceAfterFunctionKeyword": true,
"getAccessor.spaceBeforeParentheses": true, "getAccessor.spaceBeforeParentheses": true,
"ifStatement.spaceAfterIfKeyword": true, "ifStatement.spaceAfterIfKeyword": true,
"importDeclaration.spaceSurroundingNamedImports": true, "importDeclaration.spaceSurroundingNamedImports": true,

View File

@ -50,7 +50,7 @@
"SwitchCase": 1, "SwitchCase": 1,
"MemberExpression": "off", "MemberExpression": "off",
// https://github.com/eslint/eslint/issues/15299 // https://github.com/eslint/eslint/issues/15299
"ignoredNodes": ["PropertyDefinition"] "ignoredNodes": ["PropertyDefinition", "TSTypeParameterInstantiation"]
} }
], ],
"@typescript-eslint/consistent-type-assertions": [ "@typescript-eslint/consistent-type-assertions": [

View File

@ -1,5 +1,5 @@
import { NgClass, NgIf } from '@angular/common' import { NgClass, NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { ServerService } from '@app/core' import { ServerService } from '@app/core'
@ -26,20 +26,16 @@ type Prefill = {
imports: [ NgIf, FormsModule, ReactiveFormsModule, NgClass, AlertComponent ] imports: [ NgIf, FormsModule, ReactiveFormsModule, NgClass, AlertComponent ]
}) })
export class AboutContactComponent extends FormReactive implements OnInit { export class AboutContactComponent extends FormReactive implements OnInit {
protected formReactiveService = inject(FormReactiveService)
private route = inject(ActivatedRoute)
private instanceService = inject(InstanceService)
private serverService = inject(ServerService)
error: string error: string
success: string success: string
private serverConfig: HTMLServerConfig private serverConfig: HTMLServerConfig
constructor (
protected formReactiveService: FormReactiveService,
private route: ActivatedRoute,
private instanceService: InstanceService,
private serverService: ServerService
) {
super()
}
get instanceName () { get instanceName () {
return this.serverConfig.instance.name return this.serverConfig.instance.name
} }
@ -68,17 +64,17 @@ export class AboutContactComponent extends FormReactive implements OnInit {
const body = this.form.value['body'] const body = this.form.value['body']
this.instanceService.contactAdministrator(fromEmail, fromName, subject, body) this.instanceService.contactAdministrator(fromEmail, fromName, subject, body)
.subscribe({ .subscribe({
next: () => { next: () => {
this.success = $localize`Your message has been sent.` this.success = $localize`Your message has been sent.`
}, },
error: err => { error: err => {
this.error = err.status === HttpStatusCode.FORBIDDEN_403 this.error = err.status === HttpStatusCode.FORBIDDEN_403
? $localize`You already sent this form recently` ? $localize`You already sent this form recently`
: err.message : err.message
} }
}) })
} }
private prefillForm (prefill: Prefill) { private prefillForm (prefill: Prefill) {

View File

@ -1,5 +1,5 @@
import { DecimalPipe, NgFor, NgIf } from '@angular/common' import { DecimalPipe, NgFor, NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { RouterLink } from '@angular/router' import { RouterLink } from '@angular/router'
import { ComponentPagination, hasMoreItems, Notifier, RestService, ServerService } from '@app/core' import { ComponentPagination, hasMoreItems, Notifier, RestService, ServerService } from '@app/core'
import { ActorAvatarComponent } from '@app/shared/shared-actor-image/actor-avatar.component' import { ActorAvatarComponent } from '@app/shared/shared-actor-image/actor-avatar.component'
@ -29,8 +29,12 @@ import { SubscriptionImageComponent } from './subscription-image.component'
FollowerImageComponent FollowerImageComponent
] ]
}) })
export class AboutFollowsComponent implements OnInit { export class AboutFollowsComponent implements OnInit {
private server = inject(ServerService)
private restService = inject(RestService)
private notifier = inject(Notifier)
private followService = inject(InstanceFollowService)
instanceName: string instanceName: string
followers: Actor[] = [] followers: Actor[] = []
@ -58,13 +62,6 @@ export class AboutFollowsComponent implements OnInit {
order: -1 order: -1
} }
constructor (
private server: ServerService,
private restService: RestService,
private notifier: Notifier,
private followService: InstanceFollowService
) { }
ngOnInit () { ngOnInit () {
this.loadMoreFollowers(true) this.loadMoreFollowers(true)
this.loadMoreSubscriptions(true) this.loadMoreSubscriptions(true)
@ -96,20 +93,20 @@ export class AboutFollowsComponent implements OnInit {
const pagination = this.restService.componentToRestPagination(this.followersPagination) const pagination = this.restService.componentToRestPagination(this.followersPagination)
this.followService.getFollowers({ pagination, sort: this.sort, state: 'accepted' }) this.followService.getFollowers({ pagination, sort: this.sort, state: 'accepted' })
.subscribe({ .subscribe({
next: resultList => { next: resultList => {
if (reset) this.followers = [] if (reset) this.followers = []
const newFollowers = resultList.data.map(r => this.formatFollow(r.follower)) const newFollowers = resultList.data.map(r => this.formatFollow(r.follower))
this.followers = this.followers.concat(newFollowers) this.followers = this.followers.concat(newFollowers)
this.followersPagination.totalItems = resultList.total this.followersPagination.totalItems = resultList.total
}, },
error: err => this.notifier.error(err.message), error: err => this.notifier.error(err.message),
complete: () => this.loadingFollowers = false complete: () => this.loadingFollowers = false
}) })
} }
loadMoreSubscriptions (reset = false) { loadMoreSubscriptions (reset = false) {
@ -122,20 +119,20 @@ export class AboutFollowsComponent implements OnInit {
const pagination = this.restService.componentToRestPagination(this.subscriptionsPagination) const pagination = this.restService.componentToRestPagination(this.subscriptionsPagination)
this.followService.getFollowing({ pagination, sort: this.sort, state: 'accepted' }) this.followService.getFollowing({ pagination, sort: this.sort, state: 'accepted' })
.subscribe({ .subscribe({
next: resultList => { next: resultList => {
if (reset) this.subscriptions = [] if (reset) this.subscriptions = []
const newFollowings = resultList.data.map(r => this.formatFollow(r.following)) const newFollowings = resultList.data.map(r => this.formatFollow(r.following))
this.subscriptions = this.subscriptions.concat(newFollowings) this.subscriptions = this.subscriptions.concat(newFollowings)
this.subscriptionsPagination.totalItems = resultList.total this.subscriptionsPagination.totalItems = resultList.total
}, },
error: err => this.notifier.error(err.message), error: err => this.notifier.error(err.message),
complete: () => this.loadingSubscriptions = false complete: () => this.loadingSubscriptions = false
}) })
} }
private formatFollow (actor: Actor) { private formatFollow (actor: Actor) {

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { ServerService } from '@app/core' import { ServerService } from '@app/core'
import { Actor } from '@app/shared/shared-main/account/actor.model' import { Actor } from '@app/shared/shared-main/account/actor.model'
@ -9,9 +9,9 @@ import { Actor } from '@app/shared/shared-main/account/actor.model'
standalone: true standalone: true
}) })
export class FollowerImageComponent implements OnInit { export class FollowerImageComponent implements OnInit {
avatarUrl: string private server = inject(ServerService)
constructor (private server: ServerService) {} avatarUrl: string
ngOnInit () { ngOnInit () {
this.avatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.server.getHTMLConfig().instance, 30) this.avatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.server.getHTMLConfig().instance, 30)

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { ServerService } from '@app/core' import { ServerService } from '@app/core'
import { Actor } from '@app/shared/shared-main/account/actor.model' import { Actor } from '@app/shared/shared-main/account/actor.model'
@ -9,9 +9,9 @@ import { Actor } from '@app/shared/shared-main/account/actor.model'
standalone: true standalone: true
}) })
export class SubscriptionImageComponent implements OnInit { export class SubscriptionImageComponent implements OnInit {
avatarUrl: string private server = inject(ServerService)
constructor (private server: ServerService) {} avatarUrl: string
ngOnInit () { ngOnInit () {
this.avatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.server.getHTMLConfig().instance, 30) this.avatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.server.getHTMLConfig().instance, 30)

View File

@ -1,4 +1,4 @@
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core' import { Component, ElementRef, OnInit, inject, viewChild } from '@angular/core'
import { ActivatedRoute, RouterOutlet } from '@angular/router' import { ActivatedRoute, RouterOutlet } from '@angular/router'
import { AboutHTML } from '@app/shared/shared-main/instance/instance.service' import { AboutHTML } from '@app/shared/shared-main/instance/instance.service'
import { ServerConfig, ServerStats } from '@peertube/peertube-models' import { ServerConfig, ServerStats } from '@peertube/peertube-models'
@ -17,17 +17,15 @@ import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared
] ]
}) })
export class AboutInstanceComponent implements OnInit { export class AboutInstanceComponent implements OnInit {
@ViewChild('descriptionWrapper') descriptionWrapper: ElementRef<HTMLInputElement> private route = inject(ActivatedRoute)
readonly descriptionWrapper = viewChild<ElementRef<HTMLInputElement>>('descriptionWrapper')
aboutHTML: AboutHTML aboutHTML: AboutHTML
serverStats: ServerStats serverStats: ServerStats
serverConfig: ServerConfig serverConfig: ServerConfig
menuEntries: HorizontalMenuEntry[] = [] menuEntries: HorizontalMenuEntry[] = []
constructor (
private route: ActivatedRoute
) {}
ngOnInit () { ngOnInit () {
const { const {
aboutHTML, aboutHTML,

View File

@ -1,6 +1,6 @@
import { forkJoin, Observable } from 'rxjs' import { forkJoin, Observable } from 'rxjs'
import { map, switchMap } from 'rxjs/operators' import { map, switchMap } from 'rxjs/operators'
import { Injectable } from '@angular/core' import { Injectable, inject } from '@angular/core'
import { ServerService } from '@app/core' import { ServerService } from '@app/core'
import { About, ServerConfig, ServerStats } from '@peertube/peertube-models' import { About, ServerConfig, ServerStats } from '@peertube/peertube-models'
import { AboutHTML, InstanceService } from '@app/shared/shared-main/instance/instance.service' import { AboutHTML, InstanceService } from '@app/shared/shared-main/instance/instance.service'
@ -18,12 +18,9 @@ export type ResolverData = {
@Injectable() @Injectable()
export class AboutInstanceResolver { export class AboutInstanceResolver {
private instanceService = inject(InstanceService)
constructor ( private customMarkupService = inject(CustomMarkupService)
private instanceService: InstanceService, private serverService = inject(ServerService)
private customMarkupService: CustomMarkupService,
private serverService: ServerService
) {}
resolve (): Observable<ResolverData> { resolve (): Observable<ResolverData> {
return forkJoin([ return forkJoin([

View File

@ -1,5 +1,5 @@
import { NgFor, NgIf } from '@angular/common' import { NgFor, NgIf } from '@angular/common'
import { Component, OnInit, ViewChild } from '@angular/core' import { Component, OnInit, inject, viewChild } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { ServerService } from '@app/core' import { ServerService } from '@app/core'
import { AboutHTML } from '@app/shared/shared-main/instance/instance.service' import { AboutHTML } from '@app/shared/shared-main/instance/instance.service'
@ -19,7 +19,11 @@ import { ResolverData } from '../about-instance.resolver'
] ]
}) })
export class AboutInstanceHomeComponent implements OnInit { export class AboutInstanceHomeComponent implements OnInit {
@ViewChild('supportModal') supportModal: SupportModalComponent private router = inject(Router)
private route = inject(ActivatedRoute)
private serverService = inject(ServerService)
readonly supportModal = viewChild<SupportModalComponent>('supportModal')
aboutHTML: AboutHTML aboutHTML: AboutHTML
descriptionElement: HTMLDivElement descriptionElement: HTMLDivElement
@ -29,12 +33,6 @@ export class AboutInstanceHomeComponent implements OnInit {
config: HTMLServerConfig config: HTMLServerConfig
constructor (
private router: Router,
private route: ActivatedRoute,
private serverService: ServerService
) {}
ngOnInit () { ngOnInit () {
this.config = this.serverService.getHTMLConfig() this.config = this.serverService.getHTMLConfig()
@ -55,7 +53,7 @@ export class AboutInstanceHomeComponent implements OnInit {
if (!data?.isSupport) return if (!data?.isSupport) return
setTimeout(() => { setTimeout(() => {
const modal = this.supportModal.show() const modal = this.supportModal().show()
modal.hidden.subscribe(() => this.router.navigateByUrl('/about/instance/home')) modal.hidden.subscribe(() => this.router.navigateByUrl('/about/instance/home'))
}, 0) }, 0)

View File

@ -1,5 +1,5 @@
import { CommonModule } from '@angular/common' import { CommonModule } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { ServerService } from '@app/core' import { ServerService } from '@app/core'
import { AboutHTML } from '@app/shared/shared-main/instance/instance.service' import { AboutHTML } from '@app/shared/shared-main/instance/instance.service'
@ -12,12 +12,10 @@ import { PluginSelectorDirective } from '@app/shared/shared-main/plugins/plugin-
imports: [ CommonModule, PluginSelectorDirective ] imports: [ CommonModule, PluginSelectorDirective ]
}) })
export class AboutInstanceModerationComponent implements OnInit { export class AboutInstanceModerationComponent implements OnInit {
aboutHTML: AboutHTML private route = inject(ActivatedRoute)
private serverService = inject(ServerService)
constructor ( aboutHTML: AboutHTML
private route: ActivatedRoute,
private serverService: ServerService
) {}
get instanceName () { get instanceName () {
return this.serverService.getHTMLConfig().instance.name return this.serverService.getHTMLConfig().instance.name

View File

@ -1,5 +1,5 @@
import { CommonModule } from '@angular/common' import { CommonModule } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { ServerService } from '@app/core' import { ServerService } from '@app/core'
import { AboutHTML } from '@app/shared/shared-main/instance/instance.service' import { AboutHTML } from '@app/shared/shared-main/instance/instance.service'
@ -11,12 +11,10 @@ import { ResolverData } from '../about-instance.resolver'
imports: [ CommonModule ] imports: [ CommonModule ]
}) })
export class AboutInstanceTeamComponent implements OnInit { export class AboutInstanceTeamComponent implements OnInit {
aboutHTML: AboutHTML private route = inject(ActivatedRoute)
private serverService = inject(ServerService)
constructor ( aboutHTML: AboutHTML
private route: ActivatedRoute,
private serverService: ServerService
) {}
get instanceName () { get instanceName () {
return this.serverService.getHTMLConfig().instance.name return this.serverService.getHTMLConfig().instance.name

View File

@ -1,5 +1,5 @@
import { CommonModule } from '@angular/common' import { CommonModule } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { ServerService } from '@app/core' import { ServerService } from '@app/core'
import { AboutHTML } from '@app/shared/shared-main/instance/instance.service' import { AboutHTML } from '@app/shared/shared-main/instance/instance.service'
@ -13,12 +13,10 @@ import { InstanceFeaturesTableComponent } from '@app/shared/shared-instance/inst
imports: [ CommonModule, PluginSelectorDirective, InstanceFeaturesTableComponent ] imports: [ CommonModule, PluginSelectorDirective, InstanceFeaturesTableComponent ]
}) })
export class AboutInstanceTechComponent implements OnInit { export class AboutInstanceTechComponent implements OnInit {
aboutHTML: AboutHTML private route = inject(ActivatedRoute)
private serverService = inject(ServerService)
constructor ( aboutHTML: AboutHTML
private route: ActivatedRoute,
private serverService: ServerService
) {}
get instanceName () { get instanceName () {
return this.serverService.getHTMLConfig().instance.name return this.serverService.getHTMLConfig().instance.name

View File

@ -5,37 +5,37 @@
<div class="blocks" myPluginSelector pluginSelectorId="about-instance-statistics"> <div class="blocks" myPluginSelector pluginSelectorId="about-instance-statistics">
<div class="stat"> <div class="stat">
<strong>{{ stats.totalModerators + stats.totalAdmins | number }}</strong> <strong>{{ stats().totalModerators + stats().totalAdmins | number }}</strong>
<div i18n>moderators</div> <div i18n>moderators</div>
<my-global-icon iconName="moderation"></my-global-icon> <my-global-icon iconName="moderation"></my-global-icon>
</div> </div>
<div class="stat"> <div class="stat">
<strong>{{ stats.totalUsers | number }}</strong> <strong>{{ stats().totalUsers | number }}</strong>
<div i18n>users</div> <div i18n>users</div>
<my-global-icon iconName="user"></my-global-icon> <my-global-icon iconName="user"></my-global-icon>
</div> </div>
<div class="stat"> <div class="stat">
<strong>{{ stats.totalLocalVideos | number }}</strong> <strong>{{ stats().totalLocalVideos | number }}</strong>
<a routerLink="/videos/browse" [queryParams]="{ scope: 'local' }" i18n>videos</a> <a routerLink="/videos/browse" [queryParams]="{ scope: 'local' }" i18n>videos</a>
<my-global-icon iconName="videos"></my-global-icon> <my-global-icon iconName="videos"></my-global-icon>
</div> </div>
<div class="stat"> <div class="stat">
<strong>{{ stats.totalLocalVideoViews | number }}</strong> <strong>{{ stats().totalLocalVideoViews | number }}</strong>
<div i18n>views</div> <div i18n>views</div>
<my-global-icon iconName="eye-open"></my-global-icon> <my-global-icon iconName="eye-open"></my-global-icon>
</div> </div>
<div class="stat"> <div class="stat">
<strong>{{ stats.totalLocalVideoComments | number }}</strong> <strong>{{ stats().totalLocalVideoComments | number }}</strong>
<div i18n>comments</div> <div i18n>comments</div>
<my-global-icon iconName="message-circle"></my-global-icon> <my-global-icon iconName="message-circle"></my-global-icon>
</div> </div>
<div class="stat"> <div class="stat">
<strong>{{ stats.totalLocalVideoFilesSize | bytes:1 }}</strong> <strong>{{ stats().totalLocalVideoFilesSize | bytes:1 }}</strong>
<div i18n>hosted videos</div> <div i18n>hosted videos</div>
<my-global-icon iconName="film"></my-global-icon> <my-global-icon iconName="film"></my-global-icon>
</div> </div>
@ -47,7 +47,7 @@
<div class="blocks"> <div class="blocks">
<div class="usage-rule" *ngIf="config.instance.serverCountry"> <div class="usage-rule" *ngIf="config().instance.serverCountry">
<div class="icon-container"> <div class="icon-container">
<my-global-icon iconName="message-circle"></my-global-icon> <my-global-icon iconName="message-circle"></my-global-icon>
@ -57,10 +57,10 @@
</div> </div>
<div> <div>
<strong i18n>This platform has been created in {{ config.instance.serverCountry }}</strong> <strong i18n>This platform has been created in {{ config().instance.serverCountry }}</strong>
<div class="rule-content"> <div class="rule-content">
<ng-container i18n>Your content (comments, videos...) must comply with the legislation in force in this country.</ng-container> <ng-container i18n>Your content (comments, videos...) must comply with the legislation in force in this country.</ng-container>
<ng-container *ngIf="aboutHTML.codeOfConduct" i18n> You must also follow our <a routerLink="/about/instance/moderation">code of conduct</a>.</ng-container> <ng-container *ngIf="aboutHTML().codeOfConduct" i18n> You must also follow our <a routerLink="/about/instance/moderation">code of conduct</a>.</ng-container>
</div> </div>
</div> </div>
</div> </div>
@ -69,7 +69,7 @@
<div class="icon-container"> <div class="icon-container">
<my-global-icon iconName="user"></my-global-icon> <my-global-icon iconName="user"></my-global-icon>
@if (config.signup.allowed && config.signup.allowedForCurrentIP) { @if (config().signup.allowed && config().signup.allowedForCurrentIP) {
<div class="icon-status"> <div class="icon-status">
<my-global-icon iconName="tick"></my-global-icon> <my-global-icon iconName="tick"></my-global-icon>
</div> </div>
@ -81,12 +81,12 @@
</div> </div>
<div> <div>
@if (config.signup.allowed && config.signup.allowedForCurrentIP) { @if (config().signup.allowed && config().signup.allowedForCurrentIP) {
@if (config.signup.requiresApproval) { @if (config().signup.requiresApproval) {
<strong i18n>You can <a routerLink="/signup">request an account</a> on our platform</strong> <strong i18n>You can <a routerLink="/signup">request an account</a> on our platform</strong>
@if (stats.averageRegistrationRequestResponseTimeMs) { @if (stats().averageRegistrationRequestResponseTimeMs) {
<div class="rule-content" i18n>Our moderator will validate it within a {{ stats.averageRegistrationRequestResponseTimeMs | myDaysDurationFormatter }}.</div> <div class="rule-content" i18n>Our moderator will validate it within a {{ stats().averageRegistrationRequestResponseTimeMs | myDaysDurationFormatter }}.</div>
} @else { } @else {
<div class="rule-content" i18n>Our moderator will validate it within a few days.</div> <div class="rule-content" i18n>Our moderator will validate it within a few days.</div>
} }
@ -99,7 +99,7 @@
</div> </div>
</div> </div>
<div class="usage-rule" *ngIf="config.federation.enabled"> <div class="usage-rule" *ngIf="config().federation.enabled">
<div class="icon-container"> <div class="icon-container">
<my-global-icon iconName="fediverse"></my-global-icon> <my-global-icon iconName="fediverse"></my-global-icon>

View File

@ -1,5 +1,5 @@
import { CommonModule, DecimalPipe, NgIf } from '@angular/common' import { CommonModule, DecimalPipe, NgIf } from '@angular/common'
import { Component, Input } from '@angular/core' import { Component, inject, input } from '@angular/core'
import { RouterLink } from '@angular/router' import { RouterLink } from '@angular/router'
import { BytesPipe } from '@app/shared/shared-main/common/bytes.pipe' import { BytesPipe } from '@app/shared/shared-main/common/bytes.pipe'
import { DaysDurationFormatterPipe } from '@app/shared/shared-main/date/days-duration-formatter.pipe' import { DaysDurationFormatterPipe } from '@app/shared/shared-main/date/days-duration-formatter.pipe'
@ -25,13 +25,11 @@ import { AuthService } from '@app/core'
] ]
}) })
export class InstanceStatRulesComponent { export class InstanceStatRulesComponent {
@Input({ required: true }) stats: ServerStats private auth = inject(AuthService)
@Input({ required: true }) config: ServerConfig
@Input({ required: true }) aboutHTML: AboutHTML
constructor (private auth: AuthService) { readonly stats = input.required<ServerStats>()
readonly config = input.required<ServerConfig>()
} readonly aboutHTML = input.required<AboutHTML>()
canUpload () { canUpload () {
const user = this.auth.getUser() const user = this.auth.getUser()
@ -42,14 +40,16 @@ export class InstanceStatRulesComponent {
return true return true
} }
return this.config.user.videoQuota !== 0 && this.config.user.videoQuotaDaily !== 0 const config = this.config()
return config.user.videoQuota !== 0 && config.user.videoQuotaDaily !== 0
} }
canPublishLive () { canPublishLive () {
return this.config.live.enabled return this.config().live.enabled
} }
isContactFormEnabled () { isContactFormEnabled () {
return this.config.email.enabled && this.config.contactForm.enabled const config = this.config()
return config.email.enabled && config.contactForm.enabled
} }
} }

View File

@ -1,4 +1,4 @@
import { Component, AfterViewChecked } from '@angular/core' import { Component, AfterViewChecked, inject } from '@angular/core'
import { ViewportScroller } from '@angular/common' import { ViewportScroller } from '@angular/common'
@Component({ @Component({
@ -7,13 +7,10 @@ import { ViewportScroller } from '@angular/common'
styleUrls: [ './about-peertube.component.scss' ], styleUrls: [ './about-peertube.component.scss' ],
standalone: true standalone: true
}) })
export class AboutPeertubeComponent implements AfterViewChecked { export class AboutPeertubeComponent implements AfterViewChecked {
private lastScrollHash: string private viewportScroller = inject(ViewportScroller)
constructor ( private lastScrollHash: string
private viewportScroller: ViewportScroller
) {}
ngAfterViewChecked () { ngAfterViewChecked () {
if (window.location.hash && window.location.hash !== this.lastScrollHash) { if (window.location.hash && window.location.hash !== this.lastScrollHash) {

View File

@ -1,5 +1,5 @@
import { CommonModule } from '@angular/common' import { CommonModule } from '@angular/common'
import { Component, OnInit, ViewChild } from '@angular/core' import { Component, OnInit, inject, viewChild } from '@angular/core'
import { RouterOutlet } from '@angular/router' import { RouterOutlet } from '@angular/router'
import { ServerService } from '@app/core' import { ServerService } from '@app/core'
import { GlobalIconComponent } from '@app/shared/shared-icons/global-icon.component' import { GlobalIconComponent } from '@app/shared/shared-icons/global-icon.component'
@ -14,11 +14,12 @@ import { HTMLServerConfig } from '@peertube/peertube-models'
selector: 'my-about', selector: 'my-about',
templateUrl: './about.component.html', templateUrl: './about.component.html',
styleUrls: [ './about.component.scss' ], styleUrls: [ './about.component.scss' ],
imports: [ CommonModule, RouterOutlet, HorizontalMenuComponent, GlobalIconComponent, ButtonComponent, SupportModalComponent ] imports: [ CommonModule, RouterOutlet, HorizontalMenuComponent, GlobalIconComponent, ButtonComponent ]
}) })
export class AboutComponent implements OnInit { export class AboutComponent implements OnInit {
@ViewChild('supportModal') supportModal: SupportModalComponent private server = inject(ServerService)
readonly supportModal = viewChild<SupportModalComponent>('supportModal')
bannerUrl: string bannerUrl: string
avatarUrl: string avatarUrl: string
@ -27,12 +28,6 @@ export class AboutComponent implements OnInit {
config: HTMLServerConfig config: HTMLServerConfig
constructor (
private server: ServerService
) {
}
ngOnInit () { ngOnInit () {
this.config = this.server.getHTMLConfig() this.config = this.server.getHTMLConfig()

View File

@ -1,6 +1,6 @@
import { from, Subject, Subscription } from 'rxjs' import { from, Subject, Subscription } from 'rxjs'
import { concatMap, map, switchMap, tap } from 'rxjs/operators' import { concatMap, map, switchMap, tap } from 'rxjs/operators'
import { Component, OnDestroy, OnInit } from '@angular/core' import { Component, OnDestroy, OnInit, inject } from '@angular/core'
import { ComponentPagination, hasMoreItems, MarkdownService, User, UserService } from '@app/core' import { ComponentPagination, hasMoreItems, MarkdownService, User, UserService } from '@app/core'
import { SimpleMemoize } from '@app/helpers' import { SimpleMemoize } from '@app/helpers'
import { NSFWPolicyType, VideoSortField } from '@peertube/peertube-models' import { NSFWPolicyType, VideoSortField } from '@peertube/peertube-models'
@ -24,12 +24,18 @@ import { Video } from '@app/shared/shared-main/video/video.model'
imports: [ NgIf, InfiniteScrollerDirective, NgFor, ActorAvatarComponent, RouterLink, SubscribeButtonComponent, VideoMiniatureComponent ] imports: [ NgIf, InfiniteScrollerDirective, NgFor, ActorAvatarComponent, RouterLink, SubscribeButtonComponent, VideoMiniatureComponent ]
}) })
export class AccountVideoChannelsComponent implements OnInit, OnDestroy { export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
private accountService = inject(AccountService)
private videoChannelService = inject(VideoChannelService)
private videoService = inject(VideoService)
private markdown = inject(MarkdownService)
private userService = inject(UserService)
account: Account account: Account
videoChannels: VideoChannel[] = [] videoChannels: VideoChannel[] = []
videos: { [id: number]: { total: number, videos: Video[] } } = {} videos: { [id: number]: { total: number, videos: Video[] } } = {}
channelsDescriptionHTML: { [ id: number ]: string } = {} channelsDescriptionHTML: { [id: number]: string } = {}
channelPagination: ComponentPagination = { channelPagination: ComponentPagination = {
currentPage: 1, currentPage: 1,
@ -61,23 +67,15 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
private accountSub: Subscription private accountSub: Subscription
constructor (
private accountService: AccountService,
private videoChannelService: VideoChannelService,
private videoService: VideoService,
private markdown: MarkdownService,
private userService: UserService
) { }
ngOnInit () { ngOnInit () {
// Parent get the account for us // Parent get the account for us
this.accountSub = this.accountService.accountLoaded this.accountSub = this.accountService.accountLoaded
.subscribe(account => { .subscribe(account => {
this.account = account this.account = account
this.videoChannels = [] this.videoChannels = []
this.loadMoreChannels() this.loadMoreChannels()
}) })
this.userService.getAnonymousOrLoggedUser() this.userService.getAnonymousOrLoggedUser()
.subscribe(user => { .subscribe(user => {

View File

@ -1,5 +1,5 @@
import { NgIf } from '@angular/common' import { NgIf } from '@angular/common'
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core' import { Component, OnDestroy, OnInit, inject, viewChild } from '@angular/core'
import { ComponentPaginationLight, DisableForReuseHook, ScreenService } from '@app/core' import { ComponentPaginationLight, DisableForReuseHook, ScreenService } from '@app/core'
import { Account } from '@app/shared/shared-main/account/account.model' import { Account } from '@app/shared/shared-main/account/account.model'
import { AccountService } from '@app/shared/shared-main/account/account.service' import { AccountService } from '@app/shared/shared-main/account/account.service'
@ -15,7 +15,11 @@ import { VideosListComponent } from '../../shared/shared-video-miniature/videos-
imports: [ NgIf, VideosListComponent ] imports: [ NgIf, VideosListComponent ]
}) })
export class AccountVideosComponent implements OnInit, OnDestroy, DisableForReuseHook { export class AccountVideosComponent implements OnInit, OnDestroy, DisableForReuseHook {
@ViewChild('videosList') videosList: VideosListComponent private screenService = inject(ScreenService)
private accountService = inject(AccountService)
private videoService = inject(VideoService)
readonly videosList = viewChild<VideosListComponent>('videosList')
getVideosObservableFunction = this.getVideosObservable.bind(this) getVideosObservableFunction = this.getVideosObservable.bind(this)
getSyndicationItemsFunction = this.getSyndicationItems.bind(this) getSyndicationItemsFunction = this.getSyndicationItems.bind(this)
@ -29,19 +33,12 @@ export class AccountVideosComponent implements OnInit, OnDestroy, DisableForReus
private accountSub: Subscription private accountSub: Subscription
constructor (
private screenService: ScreenService,
private accountService: AccountService,
private videoService: VideoService
) {
}
ngOnInit () { ngOnInit () {
// Parent get the account for us // Parent get the account for us
this.accountSub = this.accountService.accountLoaded this.accountSub = this.accountService.accountLoaded
.subscribe(account => { .subscribe(account => {
this.account = account this.account = account
if (this.alreadyLoaded) this.videosList.reloadVideos() if (this.alreadyLoaded) this.videosList().reloadVideos()
this.alreadyLoaded = true this.alreadyLoaded = true
}) })

View File

@ -1,5 +1,5 @@
import { NgClass, NgIf } from '@angular/common' import { NgClass, NgIf } from '@angular/common'
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core' import { Component, OnDestroy, OnInit, inject, viewChild } from '@angular/core'
import { ActivatedRoute, Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router' import { ActivatedRoute, Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'
import { AuthService, MarkdownService, MetaService, Notifier, RedirectService, RestExtractor, ScreenService, UserService } from '@app/core' import { AuthService, MarkdownService, MetaService, Notifier, RedirectService, RestExtractor, ScreenService, UserService } from '@app/core'
import { Account } from '@app/shared/shared-main/account/account.model' import { Account } from '@app/shared/shared-main/account/account.model'
@ -44,7 +44,22 @@ import { SubscribeButtonComponent } from '../shared/shared-user-subscription/sub
] ]
}) })
export class AccountsComponent implements OnInit, OnDestroy { export class AccountsComponent implements OnInit, OnDestroy {
@ViewChild('accountReportModal') accountReportModal: AccountReportComponent private route = inject(ActivatedRoute)
private router = inject(Router)
private userService = inject(UserService)
private accountService = inject(AccountService)
private videoChannelService = inject(VideoChannelService)
private notifier = inject(Notifier)
private restExtractor = inject(RestExtractor)
private redirectService = inject(RedirectService)
private authService = inject(AuthService)
private videoService = inject(VideoService)
private markdown = inject(MarkdownService)
private blocklist = inject(BlocklistService)
private screenService = inject(ScreenService)
private metaService = inject(MetaService)
readonly accountReportModal = viewChild<AccountReportComponent>('accountReportModal')
account: Account account: Account
accountUser: User accountUser: User
@ -62,24 +77,6 @@ export class AccountsComponent implements OnInit, OnDestroy {
private routeSub: Subscription private routeSub: Subscription
constructor (
private route: ActivatedRoute,
private router: Router,
private userService: UserService,
private accountService: AccountService,
private videoChannelService: VideoChannelService,
private notifier: Notifier,
private restExtractor: RestExtractor,
private redirectService: RedirectService,
private authService: AuthService,
private videoService: VideoService,
private markdown: MarkdownService,
private blocklist: BlocklistService,
private screenService: ScreenService,
private metaService: MetaService
) {
}
ngOnInit () { ngOnInit () {
this.routeSub = this.route.params this.routeSub = this.route.params
.pipe( .pipe(
@ -88,10 +85,12 @@ export class AccountsComponent implements OnInit, OnDestroy {
switchMap(accountId => this.accountService.getAccount(accountId)), switchMap(accountId => this.accountService.getAccount(accountId)),
tap(account => this.onAccount(account)), tap(account => this.onAccount(account)),
switchMap(account => this.videoChannelService.listAccountVideoChannels({ account })), switchMap(account => this.videoChannelService.listAccountVideoChannels({ account })),
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, 'other', [ catchError(err =>
HttpStatusCode.BAD_REQUEST_400, this.restExtractor.redirectTo404IfNotFound(err, 'other', [
HttpStatusCode.NOT_FOUND_404 HttpStatusCode.BAD_REQUEST_400,
])) HttpStatusCode.NOT_FOUND_404
])
)
) )
.subscribe({ .subscribe({
next: videoChannels => { next: videoChannels => {
@ -187,7 +186,7 @@ export class AccountsComponent implements OnInit, OnDestroy {
} }
private showReportModal () { private showReportModal () {
this.accountReportModal.show(this.account) this.accountReportModal().show(this.account)
} }
private loadUserIfNeeded (account: Account) { private loadUserIfNeeded (account: Account) {

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { RouterOutlet } from '@angular/router' import { RouterOutlet } from '@angular/router'
import { AuthService, ServerService } from '@app/core' import { AuthService, ServerService } from '@app/core'
import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared-main/menu/horizontal-menu.component' import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared-main/menu/horizontal-menu.component'
@ -9,12 +9,10 @@ import { UserRight, UserRightType } from '@peertube/peertube-models'
imports: [ HorizontalMenuComponent, RouterOutlet ] imports: [ HorizontalMenuComponent, RouterOutlet ]
}) })
export class AdminModerationComponent implements OnInit { export class AdminModerationComponent implements OnInit {
menuEntries: HorizontalMenuEntry[] = [] private auth = inject(AuthService)
private server = inject(ServerService)
constructor ( menuEntries: HorizontalMenuEntry[] = []
private auth: AuthService,
private server: ServerService
) { }
ngOnInit () { ngOnInit () {
this.server.configReloaded.subscribe(() => this.buildMenu()) this.server.configReloaded.subscribe(() => this.buildMenu())

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { RouterOutlet } from '@angular/router' import { RouterOutlet } from '@angular/router'
import { AuthService } from '@app/core' import { AuthService } from '@app/core'
import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared-main/menu/horizontal-menu.component' import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared-main/menu/horizontal-menu.component'
@ -9,11 +9,9 @@ import { UserRight, UserRightType } from '@peertube/peertube-models'
imports: [ HorizontalMenuComponent, RouterOutlet ] imports: [ HorizontalMenuComponent, RouterOutlet ]
}) })
export class AdminOverviewComponent implements OnInit { export class AdminOverviewComponent implements OnInit {
menuEntries: HorizontalMenuEntry[] = [] private auth = inject(AuthService)
constructor ( menuEntries: HorizontalMenuEntry[] = []
private auth: AuthService
) { }
ngOnInit () { ngOnInit () {
this.buildMenu() this.buildMenu()

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { RouterOutlet } from '@angular/router' import { RouterOutlet } from '@angular/router'
import { AuthService, ServerService } from '@app/core' import { AuthService, ServerService } from '@app/core'
import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared-main/menu/horizontal-menu.component' import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared-main/menu/horizontal-menu.component'
@ -9,12 +9,10 @@ import { PluginType, UserRight, UserRightType } from '@peertube/peertube-models'
imports: [ HorizontalMenuComponent, RouterOutlet ] imports: [ HorizontalMenuComponent, RouterOutlet ]
}) })
export class AdminSettingsComponent implements OnInit { export class AdminSettingsComponent implements OnInit {
menuEntries: HorizontalMenuEntry[] = [] private auth = inject(AuthService)
private server = inject(ServerService)
constructor ( menuEntries: HorizontalMenuEntry[] = []
private auth: AuthService,
private server: ServerService
) { }
ngOnInit () { ngOnInit () {
this.server.configReloaded.subscribe(() => this.buildMenu()) this.server.configReloaded.subscribe(() => this.buildMenu())

View File

@ -1,4 +1,4 @@
<ng-container [formGroup]="form"> <ng-container [formGroup]="form()">
<div class="pt-two-cols mt-5"> <!-- cache grid --> <div class="pt-two-cols mt-5"> <!-- cache grid -->
@ -17,12 +17,12 @@
<div class="number-with-unit"> <div class="number-with-unit">
<input <input
type="number" min="0" id="cachePreviewsSize" class="form-control" type="number" min="0" id="cachePreviewsSize" class="form-control"
formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.previews.size'] }" formControlName="size" [ngClass]="{ 'input-error': formErrors()['cache.previews.size'] }"
> >
<span i18n>{getCacheSize('previews'), plural, =1 {cached image} other {cached images}}</span> <span i18n>{getCacheSize('previews'), plural, =1 {cached image} other {cached images}}</span>
</div> </div>
<div *ngIf="formErrors.cache.previews.size" class="form-error" role="alert">{{ formErrors.cache.previews.size }}</div> <div *ngIf="formErrors().cache.previews.size" class="form-error" role="alert">{{ formErrors().cache.previews.size }}</div>
</div> </div>
<div class="form-group" formGroupName="captions"> <div class="form-group" formGroupName="captions">
@ -31,12 +31,12 @@
<div class="number-with-unit"> <div class="number-with-unit">
<input <input
type="number" min="0" id="cacheCaptionsSize" class="form-control" type="number" min="0" id="cacheCaptionsSize" class="form-control"
formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.captions.size'] }" formControlName="size" [ngClass]="{ 'input-error': formErrors()['cache.captions.size'] }"
> >
<span i18n>{getCacheSize('captions'), plural, =1 {cached caption} other {cached captions}}</span> <span i18n>{getCacheSize('captions'), plural, =1 {cached caption} other {cached captions}}</span>
</div> </div>
<div *ngIf="formErrors.cache.captions.size" class="form-error" role="alert">{{ formErrors.cache.captions.size }}</div> <div *ngIf="formErrors().cache.captions.size" class="form-error" role="alert">{{ formErrors().cache.captions.size }}</div>
</div> </div>
<div class="form-group" formGroupName="torrents"> <div class="form-group" formGroupName="torrents">
@ -45,12 +45,12 @@
<div class="number-with-unit"> <div class="number-with-unit">
<input <input
type="number" min="0" id="cacheTorrentsSize" class="form-control" type="number" min="0" id="cacheTorrentsSize" class="form-control"
formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.torrents.size'] }" formControlName="size" [ngClass]="{ 'input-error': formErrors()['cache.torrents.size'] }"
> >
<span i18n>{getCacheSize('torrents'), plural, =1 {cached torrent} other {cached torrents}}</span> <span i18n>{getCacheSize('torrents'), plural, =1 {cached torrent} other {cached torrents}}</span>
</div> </div>
<div *ngIf="formErrors.cache.torrents.size" class="form-error" role="alert">{{ formErrors.cache.torrents.size }}</div> <div *ngIf="formErrors().cache.torrents.size" class="form-error" role="alert">{{ formErrors().cache.torrents.size }}</div>
</div> </div>
<div class="form-group" formGroupName="torrents"> <div class="form-group" formGroupName="torrents">
@ -59,12 +59,12 @@
<div class="number-with-unit"> <div class="number-with-unit">
<input <input
type="number" min="0" id="cacheStoryboardsSize" class="form-control" type="number" min="0" id="cacheStoryboardsSize" class="form-control"
formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.storyboards.size'] }" formControlName="size" [ngClass]="{ 'input-error': formErrors()['cache.storyboards.size'] }"
> >
<span i18n>{getCacheSize('storyboards'), plural, =1 {cached storyboard} other {cached storyboards}}</span> <span i18n>{getCacheSize('storyboards'), plural, =1 {cached storyboard} other {cached storyboards}}</span>
</div> </div>
<div *ngIf="formErrors.cache.storyboards.size" class="form-error" role="alert">{{ formErrors.cache.storyboards.size }}</div> <div *ngIf="formErrors().cache.storyboards.size" class="form-error" role="alert">{{ formErrors().cache.storyboards.size }}</div>
</div> </div>
</ng-container> </ng-container>
@ -96,10 +96,10 @@
<textarea <textarea
id="customizationJavascript" formControlName="javascript" class="form-control" dir="ltr" id="customizationJavascript" formControlName="javascript" class="form-control" dir="ltr"
[ngClass]="{ 'input-error': formErrors['instance.customizations.javascript'] }" [ngClass]="{ 'input-error': formErrors()['instance.customizations.javascript'] }"
></textarea> ></textarea>
<div *ngIf="formErrors.instance.customizations.javascript" class="form-error" role="alert">{{ formErrors.instance.customizations.javascript }}</div> <div *ngIf="formErrors().instance.customizations.javascript" class="form-error" role="alert">{{ formErrors().instance.customizations.javascript }}</div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -126,9 +126,9 @@
<textarea <textarea
id="customizationCSS" formControlName="css" class="form-control" dir="ltr" id="customizationCSS" formControlName="css" class="form-control" dir="ltr"
[ngClass]="{ 'input-error': formErrors['instance.customizations.css'] }" [ngClass]="{ 'input-error': formErrors()['instance.customizations.css'] }"
></textarea> ></textarea>
<div *ngIf="formErrors.instance.customizations.css" class="form-error" role="alert">{{ formErrors.instance.customizations.css }}</div> <div *ngIf="formErrors().instance.customizations.css" class="form-error" role="alert">{{ formErrors().instance.customizations.css }}</div>
</div> </div>
</ng-container> </ng-container>
</ng-container> </ng-container>

View File

@ -1,4 +1,4 @@
import { Component, Input } from '@angular/core' import { Component, input } from '@angular/core'
import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'
import { PeerTubeTemplateDirective } from '../../../shared/shared-main/common/peertube-template.directive' import { PeerTubeTemplateDirective } from '../../../shared/shared-main/common/peertube-template.directive'
import { HelpComponent } from '../../../shared/shared-main/buttons/help.component' import { HelpComponent } from '../../../shared/shared-main/buttons/help.component'
@ -11,10 +11,10 @@ import { NgClass, NgIf } from '@angular/common'
imports: [ FormsModule, ReactiveFormsModule, NgClass, NgIf, HelpComponent, PeerTubeTemplateDirective ] imports: [ FormsModule, ReactiveFormsModule, NgClass, NgIf, HelpComponent, PeerTubeTemplateDirective ]
}) })
export class EditAdvancedConfigurationComponent { export class EditAdvancedConfigurationComponent {
@Input() form: FormGroup readonly form = input<FormGroup>(undefined)
@Input() formErrors: any readonly formErrors = input<any>(undefined)
getCacheSize (type: 'captions' | 'previews' | 'torrents' | 'storyboards') { getCacheSize (type: 'captions' | 'previews' | 'torrents' | 'storyboards') {
return this.form.value['cache'][type]['size'] return this.form().value['cache'][type]['size']
} }
} }

View File

@ -1,4 +1,4 @@
<ng-container [formGroup]="form"> <ng-container [formGroup]="form()">
<div class="pt-two-cols mt-5"> <!-- appearance grid --> <div class="pt-two-cols mt-5"> <!-- appearance grid -->
<div class="title-col"> <div class="title-col">
<h2 i18n>APPEARANCE</h2> <h2 i18n>APPEARANCE</h2>
@ -30,7 +30,7 @@
[clearable]="false" [clearable]="false"
></my-select-custom-value> ></my-select-custom-value>
<div *ngIf="formErrors.instance.defaultClientRoute" class="form-error" role="alert">{{ formErrors.instance.defaultClientRoute }}</div> <div *ngIf="formErrors().instance.defaultClientRoute" class="form-error" role="alert">{{ formErrors().instance.defaultClientRoute }}</div>
</div> </div>
<div class="form-group" formGroupName="trending"> <div class="form-group" formGroupName="trending">
@ -47,7 +47,7 @@
</select> </select>
</div> </div>
<div *ngIf="formErrors.trending.videos.algorithms.default" class="form-error" role="alert">{{ formErrors.trending.videos.algorithms.default }}</div> <div *ngIf="formErrors().trending.videos.algorithms.default" class="form-error" role="alert">{{ formErrors().trending.videos.algorithms.default }}</div>
</ng-container> </ng-container>
</ng-container> </ng-container>
</div> </div>
@ -122,7 +122,7 @@
</select> </select>
</div> </div>
<div *ngIf="formErrors.broadcastMessage.level" class="form-error" role="alert">{{ formErrors.broadcastMessage.level }}</div> <div *ngIf="formErrors().broadcastMessage.level" class="form-error" role="alert">{{ formErrors().broadcastMessage.level }}</div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -130,10 +130,10 @@
<my-markdown-textarea <my-markdown-textarea
inputId="broadcastMessageMessage" formControlName="message" inputId="broadcastMessageMessage" formControlName="message"
[formError]="formErrors['broadcastMessage.message']" markdownType="to-unsafe-html" [formError]="formErrors()['broadcastMessage.message']" markdownType="to-unsafe-html"
></my-markdown-textarea> ></my-markdown-textarea>
<div *ngIf="formErrors.broadcastMessage.message" class="form-error" role="alert">{{ formErrors.broadcastMessage.message }}</div> <div *ngIf="formErrors().broadcastMessage.message" class="form-error" role="alert">{{ formErrors().broadcastMessage.message }}</div>
</div> </div>
</ng-container> </ng-container>
@ -185,12 +185,12 @@
<div class="number-with-unit"> <div class="number-with-unit">
<input <input
type="number" min="-1" id="signupLimit" class="form-control" type="number" min="-1" id="signupLimit" class="form-control"
formControlName="limit" [ngClass]="{ 'input-error': formErrors['signup.limit'] }" formControlName="limit" [ngClass]="{ 'input-error': formErrors()['signup.limit'] }"
> >
<span i18n>{form.value['signup']['limit'], plural, =1 {user} other {users}}</span> <span i18n>{form().value['signup']['limit'], plural, =1 {user} other {users}}</span>
</div> </div>
<div *ngIf="formErrors.signup.limit" class="form-error" role="alert">{{ formErrors.signup.limit }}</div> <div *ngIf="formErrors().signup.limit" class="form-error" role="alert">{{ formErrors().signup.limit }}</div>
<small i18n *ngIf="hasUnlimitedSignup()" class="muted small">Signup won't be limited to a fixed number of users.</small> <small i18n *ngIf="hasUnlimitedSignup()" class="muted small">Signup won't be limited to a fixed number of users.</small>
</div> </div>
@ -201,12 +201,12 @@
<div class="number-with-unit"> <div class="number-with-unit">
<input <input
type="number" min="1" id="signupMinimumAge" class="form-control" type="number" min="1" id="signupMinimumAge" class="form-control"
formControlName="minimumAge" [ngClass]="{ 'input-error': formErrors['signup.minimumAge'] }" formControlName="minimumAge" [ngClass]="{ 'input-error': formErrors()['signup.minimumAge'] }"
> >
<span i18n>{form.value['signup']['minimumAge'], plural, =1 {year old} other {years old}}</span> <span i18n>{form().value['signup']['minimumAge'], plural, =1 {year old} other {years old}}</span>
</div> </div>
<div *ngIf="formErrors.signup.minimumAge" class="form-error" role="alert">{{ formErrors.signup.minimumAge }}</div> <div *ngIf="formErrors().signup.minimumAge" class="form-error" role="alert">{{ formErrors().signup.minimumAge }}</div>
</div> </div>
</ng-container> </ng-container>
</my-peertube-checkbox> </my-peertube-checkbox>
@ -228,7 +228,7 @@
<my-user-real-quota-info class="mt-2 d-block small muted" [videoQuota]="getUserVideoQuota()"></my-user-real-quota-info> <my-user-real-quota-info class="mt-2 d-block small muted" [videoQuota]="getUserVideoQuota()"></my-user-real-quota-info>
<div *ngIf="formErrors.user.videoQuota" class="form-error" role="alert">{{ formErrors.user.videoQuota }}</div> <div *ngIf="formErrors().user.videoQuota" class="form-error" role="alert">{{ formErrors().user.videoQuota }}</div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -243,7 +243,7 @@
[clearable]="false" [clearable]="false"
></my-select-custom-value> ></my-select-custom-value>
<div *ngIf="formErrors.user.videoQuotaDaily" class="form-error" role="alert">{{ formErrors.user.videoQuotaDaily }}</div> <div *ngIf="formErrors().user.videoQuotaDaily" class="form-error" role="alert">{{ formErrors().user.videoQuotaDaily }}</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<ng-container formGroupName="history"> <ng-container formGroupName="history">
@ -281,7 +281,7 @@
<span i18n>jobs in parallel</span> <span i18n>jobs in parallel</span>
</div> </div>
<div *ngIf="formErrors.import.concurrency" class="form-error" role="alert">{{ formErrors.import.concurrency }}</div> <div *ngIf="formErrors().import.concurrency" class="form-error" role="alert">{{ formErrors().import.concurrency }}</div>
</div> </div>
<div class="form-group" formGroupName="http"> <div class="form-group" formGroupName="http">
@ -328,12 +328,12 @@
<div class="number-with-unit"> <div class="number-with-unit">
<input <input
type="number" min="1" id="videoChannelSynchronizationMaxPerUser" class="form-control" type="number" min="1" id="videoChannelSynchronizationMaxPerUser" class="form-control"
formControlName="maxPerUser" [ngClass]="{ 'input-error': formErrors['import']['videoChannelSynchronization']['maxPerUser'] }" formControlName="maxPerUser" [ngClass]="{ 'input-error': formErrors()['import']['videoChannelSynchronization']['maxPerUser'] }"
> >
<span i18n>{form.value['import']['videoChannelSynchronization']['maxPerUser'], plural, =1 {sync} other {syncs}}</span> <span i18n>{form().value['import']['videoChannelSynchronization']['maxPerUser'], plural, =1 {sync} other {syncs}}</span>
</div> </div>
<div *ngIf="formErrors.import.videoChannelSynchronization.maxPerUser" class="form-error" role="alert">{{ formErrors.import.videoChannelSynchronization.maxPerUser }}</div> <div *ngIf="formErrors().import.videoChannelSynchronization.maxPerUser" class="form-error" role="alert">{{ formErrors().import.videoChannelSynchronization.maxPerUser }}</div>
</div> </div>
</ng-container> </ng-container>
@ -426,12 +426,12 @@
<div class="number-with-unit"> <div class="number-with-unit">
<input <input
type="number" min="1" id="videoChannelsMaxPerUser" class="form-control" type="number" min="1" id="videoChannelsMaxPerUser" class="form-control"
formControlName="maxPerUser" [ngClass]="{ 'input-error': formErrors['videoChannels.maxPerUser'] }" formControlName="maxPerUser" [ngClass]="{ 'input-error': formErrors()['videoChannels.maxPerUser'] }"
> >
<span i18n>{form.value['videoChannels']['maxPerUser'], plural, =1 {channel} other {channels}}</span> <span i18n>{form().value['videoChannels']['maxPerUser'], plural, =1 {channel} other {channels}}</span>
</div> </div>
<div *ngIf="formErrors.videoChannels.maxPerUser" class="form-error" role="alert">{{ formErrors.videoChannels.maxPerUser }}</div> <div *ngIf="formErrors().videoChannels.maxPerUser" class="form-error" role="alert">{{ formErrors().videoChannels.maxPerUser }}</div>
</div> </div>
</div> </div>
</div> </div>
@ -490,10 +490,10 @@
<input <input
type="text" id="searchIndexUrl" class="form-control" type="text" id="searchIndexUrl" class="form-control"
formControlName="url" [ngClass]="{ 'input-error': formErrors['search.searchIndex.url'] }" formControlName="url" [ngClass]="{ 'input-error': formErrors()['search.searchIndex.url'] }"
> >
<div *ngIf="formErrors.search.searchIndex.url" class="form-error" role="alert">{{ formErrors.search.searchIndex.url }}</div> <div *ngIf="formErrors().search.searchIndex.url" class="form-error" role="alert">{{ formErrors().search.searchIndex.url }}</div>
</div> </div>
<div class="mt-3"> <div class="mt-3">
@ -577,7 +577,7 @@
[clearable]="false" [clearable]="false"
></my-select-custom-value> ></my-select-custom-value>
<div *ngIf="formErrors.export.users.maxUserVideoQuota" class="form-error" role="alert">{{ formErrors.export.users.maxUserVideoQuota }}</div> <div *ngIf="formErrors().export.users.maxUserVideoQuota" class="form-error" role="alert">{{ formErrors().export.users.maxUserVideoQuota }}</div>
</div> </div>
<div class="form-group" [ngClass]="getDisabledExportUsersClass()"> <div class="form-group" [ngClass]="getDisabledExportUsersClass()">
@ -587,7 +587,7 @@
<div i18n class="mt-1 small muted">The archive file is deleted after this period.</div> <div i18n class="mt-1 small muted">The archive file is deleted after this period.</div>
<div *ngIf="formErrors.export.users.exportExpiration" class="form-error" role="alert">{{ formErrors.export.users.exportExpiration }}</div> <div *ngIf="formErrors().export.users.exportExpiration" class="form-error" role="alert">{{ formErrors().export.users.exportExpiration }}</div>
</div> </div>
</ng-container> </ng-container>
@ -663,9 +663,9 @@
<label i18n for="followingsInstanceAutoFollowIndexUrl">Index URL</label> <label i18n for="followingsInstanceAutoFollowIndexUrl">Index URL</label>
<input <input
type="text" id="followingsInstanceAutoFollowIndexUrl" class="form-control" type="text" id="followingsInstanceAutoFollowIndexUrl" class="form-control"
formControlName="indexUrl" [ngClass]="{ 'input-error': formErrors['followings.instance.autoFollowIndex.indexUrl'] }" formControlName="indexUrl" [ngClass]="{ 'input-error': formErrors()['followings.instance.autoFollowIndex.indexUrl'] }"
> >
<div *ngIf="formErrors.followings.instance.autoFollowIndex.indexUrl" class="form-error" role="alert">{{ formErrors.followings.instance.autoFollowIndex.indexUrl }}</div> <div *ngIf="formErrors().followings.instance.autoFollowIndex.indexUrl" class="form-error" role="alert">{{ formErrors().followings.instance.autoFollowIndex.indexUrl }}</div>
</div> </div>
</ng-container> </ng-container>
</my-peertube-checkbox> </my-peertube-checkbox>
@ -690,10 +690,10 @@
<input <input
type="text" id="adminEmail" class="form-control" type="text" id="adminEmail" class="form-control"
formControlName="email" [ngClass]="{ 'input-error': formErrors['admin.email'] }" formControlName="email" [ngClass]="{ 'input-error': formErrors()['admin.email'] }"
> >
<div *ngIf="formErrors.admin.email" class="form-error" role="alert">{{ formErrors.admin.email }}</div> <div *ngIf="formErrors().admin.email" class="form-error" role="alert">{{ formErrors().admin.email }}</div>
</div> </div>
<div class="form-group" formGroupName="contactForm"> <div class="form-group" formGroupName="contactForm">
@ -731,10 +731,10 @@
<input <input
type="text" id="servicesTwitterUsername" class="form-control" type="text" id="servicesTwitterUsername" class="form-control"
formControlName="username" [ngClass]="{ 'input-error': formErrors['services.twitter.username'] }" formControlName="username" [ngClass]="{ 'input-error': formErrors()['services.twitter.username'] }"
> >
<div *ngIf="formErrors.services.twitter.username" class="form-error" role="alert">{{ formErrors.services.twitter.username }}</div> <div *ngIf="formErrors().services.twitter.username" class="form-error" role="alert">{{ formErrors().services.twitter.username }}</div>
</div> </div>
</ng-container> </ng-container>

View File

@ -1,5 +1,5 @@
import { NgClass, NgFor, NgIf } from '@angular/common' import { NgClass, NgIf } from '@angular/common'
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core' import { Component, OnChanges, OnInit, SimpleChanges, inject, input } from '@angular/core'
import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'
import { RouterLink } from '@angular/router' import { RouterLink } from '@angular/router'
import { ThemeService } from '@app/core' import { ThemeService } from '@app/core'
@ -23,7 +23,6 @@ import { ConfigService } from '../shared/config.service'
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
RouterLink, RouterLink,
NgFor,
SelectCustomValueComponent, SelectCustomValueComponent,
NgIf, NgIf,
PeertubeCheckboxComponent, PeertubeCheckboxComponent,
@ -36,10 +35,13 @@ import { ConfigService } from '../shared/config.service'
] ]
}) })
export class EditBasicConfigurationComponent implements OnInit, OnChanges { export class EditBasicConfigurationComponent implements OnInit, OnChanges {
@Input() form: FormGroup private configService = inject(ConfigService)
@Input() formErrors: any private themeService = inject(ThemeService)
@Input() serverConfig: HTMLServerConfig readonly form = input<FormGroup>(undefined)
readonly formErrors = input<any>(undefined)
readonly serverConfig = input<HTMLServerConfig>(undefined)
signupAlertMessage: string signupAlertMessage: string
defaultLandingPageOptions: SelectOptionsItem[] = [] defaultLandingPageOptions: SelectOptionsItem[] = []
@ -48,11 +50,6 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
exportExpirationOptions: SelectOptionsItem[] = [] exportExpirationOptions: SelectOptionsItem[] = []
exportMaxUserVideoQuotaOptions: SelectOptionsItem[] = [] exportMaxUserVideoQuotaOptions: SelectOptionsItem[] = []
constructor (
private configService: ConfigService,
private themeService: ThemeService
) {}
ngOnInit () { ngOnInit () {
this.buildLandingPageOptions() this.buildLandingPageOptions()
this.checkSignupField() this.checkSignupField()
@ -81,7 +78,7 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
} }
countExternalAuth () { countExternalAuth () {
return this.serverConfig.plugin.registeredExternalAuths.length return this.serverConfig().plugin.registeredExternalAuths.length
} }
getVideoQuotaOptions () { getVideoQuotaOptions () {
@ -93,18 +90,18 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
} }
doesTrendingVideosAlgorithmsEnabledInclude (algorithm: string) { doesTrendingVideosAlgorithmsEnabledInclude (algorithm: string) {
const enabled = this.form.value['trending']['videos']['algorithms']['enabled'] const enabled = this.form().value['trending']['videos']['algorithms']['enabled']
if (!Array.isArray(enabled)) return false if (!Array.isArray(enabled)) return false
return !!enabled.find((e: string) => e === algorithm) return !!enabled.find((e: string) => e === algorithm)
} }
getUserVideoQuota () { getUserVideoQuota () {
return this.form.value['user']['videoQuota'] return this.form().value['user']['videoQuota']
} }
isExportUsersEnabled () { isExportUsersEnabled () {
return this.form.value['export']['users']['enabled'] === true return this.form().value['export']['users']['enabled'] === true
} }
getDisabledExportUsersClass () { getDisabledExportUsersClass () {
@ -112,7 +109,7 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
} }
isSignupEnabled () { isSignupEnabled () {
return this.form.value['signup']['enabled'] === true return this.form().value['signup']['enabled'] === true
} }
getDisabledSignupClass () { getDisabledSignupClass () {
@ -120,19 +117,19 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
} }
isImportVideosHttpEnabled (): boolean { isImportVideosHttpEnabled (): boolean {
return this.form.value['import']['videos']['http']['enabled'] === true return this.form().value['import']['videos']['http']['enabled'] === true
} }
importSynchronizationChecked () { importSynchronizationChecked () {
return this.isImportVideosHttpEnabled() && this.form.value['import']['videoChannelSynchronization']['enabled'] return this.isImportVideosHttpEnabled() && this.form().value['import']['videoChannelSynchronization']['enabled']
} }
hasUnlimitedSignup () { hasUnlimitedSignup () {
return this.form.value['signup']['limit'] === -1 return this.form().value['signup']['limit'] === -1
} }
isSearchIndexEnabled () { isSearchIndexEnabled () {
return this.form.value['search']['searchIndex']['enabled'] === true return this.form().value['search']['searchIndex']['enabled'] === true
} }
getDisabledSearchIndexClass () { getDisabledSearchIndexClass () {
@ -142,7 +139,7 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
isTranscriptionEnabled () { isTranscriptionEnabled () {
return this.form.value['videoTranscription']['enabled'] === true return this.form().value['videoTranscription']['enabled'] === true
} }
getTranscriptionRunnerDisabledClass () { getTranscriptionRunnerDisabledClass () {
@ -152,13 +149,13 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
isAutoFollowIndexEnabled () { isAutoFollowIndexEnabled () {
return this.form.value['followings']['instance']['autoFollowIndex']['enabled'] === true return this.form().value['followings']['instance']['autoFollowIndex']['enabled'] === true
} }
buildLandingPageOptions () { buildLandingPageOptions () {
let links: { label: string, path: string }[] = [] let links: { label: string, path: string }[] = []
if (this.serverConfig.homepage.enabled) { if (this.serverConfig().homepage.enabled) {
links.push({ label: $localize`Home`, path: '/home' }) links.push({ label: $localize`Home`, path: '/home' })
} }
@ -176,11 +173,11 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
} }
private checkImportSyncField () { private checkImportSyncField () {
const importSyncControl = this.form.get('import.videoChannelSynchronization.enabled') const importSyncControl = this.form().get('import.videoChannelSynchronization.enabled')
const importVideosHttpControl = this.form.get('import.videos.http.enabled') const importVideosHttpControl = this.form().get('import.videos.http.enabled')
importVideosHttpControl.valueChanges importVideosHttpControl.valueChanges
.subscribe((httpImportEnabled) => { .subscribe(httpImportEnabled => {
importSyncControl.setValue(httpImportEnabled && importSyncControl.value) importSyncControl.setValue(httpImportEnabled && importSyncControl.value)
if (httpImportEnabled) { if (httpImportEnabled) {
importSyncControl.enable() importSyncControl.enable()
@ -191,16 +188,17 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
} }
private checkSignupField () { private checkSignupField () {
const signupControl = this.form.get('signup.enabled') const signupControl = this.form().get('signup.enabled')
signupControl.valueChanges signupControl.valueChanges
.pipe(pairwise()) .pipe(pairwise())
.subscribe(([ oldValue, newValue ]) => { .subscribe(([ oldValue, newValue ]) => {
if (oldValue === false && newValue === true) { if (oldValue === false && newValue === true) {
/* eslint-disable max-len */ /* eslint-disable max-len */
this.signupAlertMessage = $localize`You enabled signup: we automatically enabled the "Block new videos automatically" checkbox of the "Videos" section just below.` this.signupAlertMessage =
$localize`You enabled signup: we automatically enabled the "Block new videos automatically" checkbox of the "Videos" section just below.`
this.form.patchValue({ this.form().patchValue({
autoBlacklist: { autoBlacklist: {
videos: { videos: {
ofUsers: { ofUsers: {

View File

@ -1,5 +1,5 @@
import { NgFor, NgIf } from '@angular/common' import { NgFor, NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { ConfigService } from '@app/+admin/config/shared/config.service' import { ConfigService } from '@app/+admin/config/shared/config.service'
@ -73,6 +73,15 @@ type ComponentCustomConfig = CustomConfig & {
] ]
}) })
export class EditCustomConfigComponent extends FormReactive implements OnInit { export class EditCustomConfigComponent extends FormReactive implements OnInit {
protected formReactiveService = inject(FormReactiveService)
private router = inject(Router)
private route = inject(ActivatedRoute)
private notifier = inject(Notifier)
private configService = inject(ConfigService)
private customPage = inject(CustomPageService)
private serverService = inject(ServerService)
private editConfigurationService = inject(EditConfigurationService)
activeNav: string activeNav: string
customConfig: ComponentCustomConfig customConfig: ComponentCustomConfig
@ -83,23 +92,10 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
languageItems: SelectOptionsItem[] = [] languageItems: SelectOptionsItem[] = []
categoryItems: SelectOptionsItem[] = [] categoryItems: SelectOptionsItem[] = []
constructor (
protected formReactiveService: FormReactiveService,
private router: Router,
private route: ActivatedRoute,
private notifier: Notifier,
private configService: ConfigService,
private customPage: CustomPageService,
private serverService: ServerService,
private editConfigurationService: EditConfigurationService
) {
super()
}
ngOnInit () { ngOnInit () {
this.serverConfig = this.serverService.getHTMLConfig() this.serverConfig = this.serverService.getHTMLConfig()
const formGroupData: { [key in keyof ComponentCustomConfig ]: any } = { const formGroupData: { [key in keyof ComponentCustomConfig]: any } = {
instance: { instance: {
name: INSTANCE_NAME_VALIDATOR, name: INSTANCE_NAME_VALIDATOR,
shortDescription: INSTANCE_SHORT_DESCRIPTION_VALIDATOR, shortDescription: INSTANCE_SHORT_DESCRIPTION_VALIDATOR,
@ -193,7 +189,6 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
videoChannelSynchronization: { videoChannelSynchronization: {
enabled: null, enabled: null,
maxPerUser: MAX_SYNC_PER_USER maxPerUser: MAX_SYNC_PER_USER
}, },
users: { users: {
enabled: null enabled: null

View File

@ -1,4 +1,4 @@
<ng-container [formGroup]="form"> <ng-container [formGroup]="form()">
<ng-container formGroupName="instanceCustomHomepage"> <ng-container formGroupName="instanceCustomHomepage">
@ -18,11 +18,11 @@
<my-markdown-textarea <my-markdown-textarea
inputId="instanceCustomHomepageContent" formControlName="content" inputId="instanceCustomHomepageContent" formControlName="content"
[customMarkdownRenderer]="getCustomMarkdownRenderer()" [debounceTime]="500" [customMarkdownRenderer]="getCustomMarkdownRenderer()" [debounceTime]="500"
[formError]="formErrors['instanceCustomHomepage.content']" [formError]="formErrors()['instanceCustomHomepage.content']"
dir="ltr" dir="ltr"
></my-markdown-textarea> ></my-markdown-textarea>
<div *ngIf="formErrors.instanceCustomHomepage.content" class="form-error" role="alert">{{ formErrors.instanceCustomHomepage.content }}</div> <div *ngIf="formErrors().instanceCustomHomepage.content" class="form-error" role="alert">{{ formErrors().instanceCustomHomepage.content }}</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,4 +1,4 @@
import { Component, Input } from '@angular/core' import { Component, inject, input } from '@angular/core'
import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgIf } from '@angular/common' import { NgIf } from '@angular/common'
import { MarkdownTextareaComponent } from '../../../shared/shared-forms/markdown-textarea.component' import { MarkdownTextareaComponent } from '../../../shared/shared-forms/markdown-textarea.component'
@ -12,15 +12,13 @@ import { CustomMarkupService } from '@app/shared/shared-custom-markup/custom-mar
imports: [ FormsModule, ReactiveFormsModule, CustomMarkupHelpComponent, MarkdownTextareaComponent, NgIf ] imports: [ FormsModule, ReactiveFormsModule, CustomMarkupHelpComponent, MarkdownTextareaComponent, NgIf ]
}) })
export class EditHomepageComponent { export class EditHomepageComponent {
@Input() form: FormGroup private customMarkup = inject(CustomMarkupService)
@Input() formErrors: any
readonly form = input<FormGroup>(undefined)
readonly formErrors = input<any>(undefined)
customMarkdownRenderer: (text: string) => Promise<HTMLElement> customMarkdownRenderer: (text: string) => Promise<HTMLElement>
constructor (private customMarkup: CustomMarkupService) {
}
getCustomMarkdownRenderer () { getCustomMarkdownRenderer () {
return this.customMarkup.getCustomMarkdownRenderer() return this.customMarkup.getCustomMarkdownRenderer()
} }

View File

@ -1,4 +1,4 @@
<ng-container [formGroup]="form"> <ng-container [formGroup]="form()">
<ng-container formGroupName="instance"> <ng-container formGroupName="instance">
@ -41,10 +41,10 @@
<input <input
type="text" id="instanceName" class="form-control" type="text" id="instanceName" class="form-control"
formControlName="name" [ngClass]="{ 'input-error': formErrors.instance.name }" formControlName="name" [ngClass]="{ 'input-error': formErrors().instance.name }"
> >
<div *ngIf="formErrors.instance.name" class="form-error" role="alert">{{ formErrors.instance.name }}</div> <div *ngIf="formErrors().instance.name" class="form-error" role="alert">{{ formErrors().instance.name }}</div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -52,10 +52,10 @@
<textarea <textarea
id="instanceShortDescription" formControlName="shortDescription" class="form-control small" id="instanceShortDescription" formControlName="shortDescription" class="form-control small"
[ngClass]="{ 'input-error': formErrors['instance.shortDescription'] }" [ngClass]="{ 'input-error': formErrors()['instance.shortDescription'] }"
></textarea> ></textarea>
<div *ngIf="formErrors.instance.shortDescription" class="form-error" role="alert">{{ formErrors.instance.shortDescription }}</div> <div *ngIf="formErrors().instance.shortDescription" class="form-error" role="alert">{{ formErrors().instance.shortDescription }}</div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -67,7 +67,7 @@
<my-markdown-textarea <my-markdown-textarea
inputId="instanceDescription" formControlName="description" inputId="instanceDescription" formControlName="description"
[customMarkdownRenderer]="getCustomMarkdownRenderer()" [debounceTime]="500" [customMarkdownRenderer]="getCustomMarkdownRenderer()" [debounceTime]="500"
[formError]="formErrors['instance.description']" [formError]="formErrors()['instance.description']"
></my-markdown-textarea> ></my-markdown-textarea>
</div> </div>
@ -77,7 +77,7 @@
<div> <div>
<my-select-checkbox <my-select-checkbox
inputId="instanceCategories" inputId="instanceCategories"
formControlName="categories" [availableItems]="categoryItems" formControlName="categories" [availableItems]="categoryItems()"
[selectableGroup]="false" [selectableGroup]="false"
i18n-placeholder placeholder="Add a new category" i18n-placeholder placeholder="Add a new category"
> >
@ -91,7 +91,7 @@
<div> <div>
<my-select-checkbox <my-select-checkbox
inputId="instanceLanguages" inputId="instanceLanguages"
formControlName="languages" [availableItems]="languageItems" formControlName="languages" [availableItems]="languageItems()"
[selectableGroup]="false" [selectableGroup]="false"
i18n-placeholder placeholder="Add a new language" i18n-placeholder placeholder="Add a new language"
> >
@ -105,10 +105,10 @@
<input <input
type="text" id="instanceServerCountry" class="form-control" type="text" id="instanceServerCountry" class="form-control"
formControlName="serverCountry" [ngClass]="{ 'input-error': formErrors.instance.serverCountry }" formControlName="serverCountry" [ngClass]="{ 'input-error': formErrors().instance.serverCountry }"
> >
<div *ngIf="formErrors.instance.serverCountry" class="form-error" role="alert">{{ formErrors.instance.serverCountry }}</div> <div *ngIf="formErrors().instance.serverCountry" class="form-error" role="alert">{{ formErrors().instance.serverCountry }}</div>
</div> </div>
</div> </div>
@ -130,7 +130,7 @@
<my-markdown-textarea <my-markdown-textarea
inputId="instanceSupportText" formControlName="text" markdownType="enhanced" inputId="instanceSupportText" formControlName="text" markdownType="enhanced"
[formError]="formErrors['instance.support.text']" [formError]="formErrors()['instance.support.text']"
></my-markdown-textarea> ></my-markdown-textarea>
</div> </div>
@ -141,10 +141,10 @@
<input <input
type="text" id="instanceSocialExternalLink" class="form-control" type="text" id="instanceSocialExternalLink" class="form-control"
formControlName="externalLink" [ngClass]="{ 'input-error': formErrors.instance.social.externalLink }" formControlName="externalLink" [ngClass]="{ 'input-error': formErrors().instance.social.externalLink }"
> >
<div *ngIf="formErrors.instance.social.externalLink" class="form-error" role="alert">{{ formErrors.instance.social.externalLink }}</div> <div *ngIf="formErrors().instance.social.externalLink" class="form-error" role="alert">{{ formErrors().instance.social.externalLink }}</div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -152,10 +152,10 @@
<input <input
type="text" id="instanceSocialMastodonLink" class="form-control" type="text" id="instanceSocialMastodonLink" class="form-control"
formControlName="mastodonLink" [ngClass]="{ 'input-error': formErrors.instance.social.mastodonLink }" formControlName="mastodonLink" [ngClass]="{ 'input-error': formErrors().instance.social.mastodonLink }"
> >
<div *ngIf="formErrors.instance.social.mastodonLink" class="form-error" role="alert">{{ formErrors.instance.social.mastodonLink }}</div> <div *ngIf="formErrors().instance.social.mastodonLink" class="form-error" role="alert">{{ formErrors().instance.social.mastodonLink }}</div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -163,10 +163,10 @@
<input <input
type="text" id="instanceSocialBlueskyLink" class="form-control" type="text" id="instanceSocialBlueskyLink" class="form-control"
formControlName="blueskyLink" [ngClass]="{ 'input-error': formErrors.instance.social.blueskyLink }" formControlName="blueskyLink" [ngClass]="{ 'input-error': formErrors().instance.social.blueskyLink }"
> >
<div *ngIf="formErrors.instance.social.blueskyLink" class="form-error" role="alert">{{ formErrors.instance.social.blueskyLink }}</div> <div *ngIf="formErrors().instance.social.blueskyLink" class="form-error" role="alert">{{ formErrors().instance.social.blueskyLink }}</div>
</div> </div>
</ng-container> </ng-container>
@ -217,7 +217,7 @@
</select> </select>
</div> </div>
<div *ngIf="formErrors.instance.defaultNSFWPolicy" class="form-error" role="alert">{{ formErrors.instance.defaultNSFWPolicy }}</div> <div *ngIf="formErrors().instance.defaultNSFWPolicy" class="form-error" role="alert">{{ formErrors().instance.defaultNSFWPolicy }}</div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -225,7 +225,7 @@
<my-markdown-textarea <my-markdown-textarea
inputId="instanceTerms" formControlName="terms" markdownType="enhanced" inputId="instanceTerms" formControlName="terms" markdownType="enhanced"
[formError]="formErrors['instance.terms']" [formError]="formErrors()['instance.terms']"
></my-markdown-textarea> ></my-markdown-textarea>
</div> </div>
@ -234,7 +234,7 @@
<my-markdown-textarea <my-markdown-textarea
inputId="instanceCodeOfConduct" formControlName="codeOfConduct" markdownType="enhanced" inputId="instanceCodeOfConduct" formControlName="codeOfConduct" markdownType="enhanced"
[formError]="formErrors['instance.codeOfConduct']" [formError]="formErrors()['instance.codeOfConduct']"
></my-markdown-textarea> ></my-markdown-textarea>
</div> </div>
@ -244,7 +244,7 @@
<my-markdown-textarea <my-markdown-textarea
inputId="instanceModerationInformation" formControlName="moderationInformation" markdownType="enhanced" inputId="instanceModerationInformation" formControlName="moderationInformation" markdownType="enhanced"
[formError]="formErrors['instance.moderationInformation']" [formError]="formErrors()['instance.moderationInformation']"
></my-markdown-textarea> ></my-markdown-textarea>
</div> </div>
@ -264,7 +264,7 @@
<my-markdown-textarea <my-markdown-textarea
inputId="instanceAdministrator" formControlName="administrator" markdownType="enhanced" inputId="instanceAdministrator" formControlName="administrator" markdownType="enhanced"
[formError]="formErrors['instance.administrator']" [formError]="formErrors()['instance.administrator']"
></my-markdown-textarea> ></my-markdown-textarea>
</div> </div>
@ -274,7 +274,7 @@
<my-markdown-textarea <my-markdown-textarea
inputId="instanceCreationReason" formControlName="creationReason" markdownType="enhanced" inputId="instanceCreationReason" formControlName="creationReason" markdownType="enhanced"
[formError]="formErrors['instance.creationReason']" [formError]="formErrors()['instance.creationReason']"
></my-markdown-textarea> ></my-markdown-textarea>
</div> </div>
@ -284,7 +284,7 @@
<my-markdown-textarea <my-markdown-textarea
inputId="instanceMaintenanceLifetime" formControlName="maintenanceLifetime" markdownType="enhanced" inputId="instanceMaintenanceLifetime" formControlName="maintenanceLifetime" markdownType="enhanced"
[formError]="formErrors['instance.maintenanceLifetime']" [formError]="formErrors()['instance.maintenanceLifetime']"
></my-markdown-textarea> ></my-markdown-textarea>
</div> </div>
@ -294,7 +294,7 @@
<my-markdown-textarea <my-markdown-textarea
inputId="instanceBusinessModel" formControlName="businessModel" markdownType="enhanced" inputId="instanceBusinessModel" formControlName="businessModel" markdownType="enhanced"
[formError]="formErrors['instance.businessModel']" [formError]="formErrors()['instance.businessModel']"
></my-markdown-textarea> ></my-markdown-textarea>
</div> </div>
@ -314,7 +314,7 @@
<my-markdown-textarea <my-markdown-textarea
inputId="instanceHardwareInformation" formControlName="hardwareInformation" markdownType="enhanced" inputId="instanceHardwareInformation" formControlName="hardwareInformation" markdownType="enhanced"
[formError]="formErrors['instance.hardwareInformation']" [formError]="formErrors()['instance.hardwareInformation']"
></my-markdown-textarea> ></my-markdown-textarea>
</div> </div>

View File

@ -1,6 +1,6 @@
import { NgClass, NgIf } from '@angular/common' import { NgClass, NgIf } from '@angular/common'
import { HttpErrorResponse } from '@angular/common/http' import { HttpErrorResponse } from '@angular/common/http'
import { Component, Input, OnInit } from '@angular/core' import { Component, OnInit, inject, input } from '@angular/core'
import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'
import { RouterLink } from '@angular/router' import { RouterLink } from '@angular/router'
import { Notifier, ServerService } from '@app/core' import { Notifier, ServerService } from '@app/core'
@ -40,26 +40,22 @@ import { HelpComponent } from '../../../shared/shared-main/buttons/help.componen
] ]
}) })
export class EditInstanceInformationComponent implements OnInit { export class EditInstanceInformationComponent implements OnInit {
@Input() form: FormGroup private customMarkup = inject(CustomMarkupService)
@Input() formErrors: any private notifier = inject(Notifier)
private instanceService = inject(InstanceService)
private server = inject(ServerService)
@Input() languageItems: SelectOptionsItem[] = [] readonly form = input<FormGroup>(undefined)
@Input() categoryItems: SelectOptionsItem[] = [] readonly formErrors = input<any>(undefined)
readonly languageItems = input<SelectOptionsItem[]>([])
readonly categoryItems = input<SelectOptionsItem[]>([])
instanceBannerUrl: string instanceBannerUrl: string
instanceAvatars: ActorImage[] = [] instanceAvatars: ActorImage[] = []
private serverConfig: HTMLServerConfig private serverConfig: HTMLServerConfig
constructor (
private customMarkup: CustomMarkupService,
private notifier: Notifier,
private instanceService: InstanceService,
private server: ServerService
) {
}
get instanceName () { get instanceName () {
return this.server.getHTMLConfig().instance.name return this.server.getHTMLConfig().instance.name
} }
@ -139,5 +135,4 @@ export class EditInstanceInformationComponent implements OnInit {
this.updateActorImages() this.updateActorImages()
}) })
} }
} }

View File

@ -1,4 +1,4 @@
<ng-container [formGroup]="form"> <ng-container [formGroup]="form()">
<div class="pt-two-cols mt-5"> <div class="pt-two-cols mt-5">
<div class="title-col"> <div class="title-col">
@ -52,10 +52,10 @@
<div class="number-with-unit"> <div class="number-with-unit">
<input type="number" id="liveMaxInstanceLives" formControlName="maxInstanceLives" /> <input type="number" id="liveMaxInstanceLives" formControlName="maxInstanceLives" />
<span i18n>{form.value['live']['maxInstanceLives'], plural, =1 {live} other {lives}}</span> <span i18n>{form().value['live']['maxInstanceLives'], plural, =1 {live} other {lives}}</span>
</div> </div>
<div *ngIf="formErrors.live.maxInstanceLives" class="form-error" role="alert">{{ formErrors.live.maxInstanceLives }}</div> <div *ngIf="formErrors().live.maxInstanceLives" class="form-error" role="alert">{{ formErrors().live.maxInstanceLives }}</div>
</div> </div>
<div class="form-group" [ngClass]="getDisabledLiveClass()"> <div class="form-group" [ngClass]="getDisabledLiveClass()">
@ -64,10 +64,10 @@
<div class="number-with-unit"> <div class="number-with-unit">
<input type="number" id="liveMaxUserLives" formControlName="maxUserLives" /> <input type="number" id="liveMaxUserLives" formControlName="maxUserLives" />
<span i18n>{form.value['live']['maxUserLives'], plural, =1 {live} other {lives}}</span> <span i18n>{form().value['live']['maxUserLives'], plural, =1 {live} other {lives}}</span>
</div> </div>
<div *ngIf="formErrors.live.maxUserLives" class="form-error" role="alert">{{ formErrors.live.maxUserLives }}</div> <div *ngIf="formErrors().live.maxUserLives" class="form-error" role="alert">{{ formErrors().live.maxUserLives }}</div>
</div> </div>
<div class="form-group" [ngClass]="getDisabledLiveClass()"> <div class="form-group" [ngClass]="getDisabledLiveClass()">
@ -75,7 +75,7 @@
<my-select-options inputId="liveMaxDuration" [items]="liveMaxDurationOptions" formControlName="maxDuration"></my-select-options> <my-select-options inputId="liveMaxDuration" [items]="liveMaxDurationOptions" formControlName="maxDuration"></my-select-options>
<div *ngIf="formErrors.live.maxDuration" class="form-error" role="alert">{{ formErrors.live.maxDuration }}</div> <div *ngIf="formErrors().live.maxDuration" class="form-error" role="alert">{{ formErrors().live.maxDuration }}</div>
</div> </div>
</ng-container> </ng-container>
@ -123,7 +123,7 @@
<span>FPS</span> <span>FPS</span>
</div> </div>
<div *ngIf="formErrors.live.transcoding.fps.max" class="form-error" role="alert">{{ formErrors.live.transcoding.fps.max }}</div> <div *ngIf="formErrors().live.transcoding.fps.max" class="form-error" role="alert">{{ formErrors().live.transcoding.fps.max }}</div>
</div> </div>
<div class="ms-2 mt-3"> <div class="ms-2 mt-3">
@ -193,7 +193,7 @@
formControlName="threads" formControlName="threads"
[clearable]="false" [clearable]="false"
></my-select-custom-value> ></my-select-custom-value>
<div *ngIf="formErrors.live.transcoding.threads" class="form-error" role="alert">{{ formErrors.live.transcoding.threads }}</div> <div *ngIf="formErrors().live.transcoding.threads" class="form-error" role="alert">{{ formErrors().live.transcoding.threads }}</div>
</div> </div>
<div class="form-group mt-4" [ngClass]="getDisabledLiveLocalTranscodingClass()"> <div class="form-group mt-4" [ngClass]="getDisabledLiveLocalTranscodingClass()">
@ -202,7 +202,7 @@
<my-select-options inputId="liveTranscodingProfile" formControlName="profile" [items]="transcodingProfiles"></my-select-options> <my-select-options inputId="liveTranscodingProfile" formControlName="profile" [items]="transcodingProfiles"></my-select-options>
<div *ngIf="formErrors.live.transcoding.profile" class="form-error" role="alert">{{ formErrors.live.transcoding.profile }}</div> <div *ngIf="formErrors().live.transcoding.profile" class="form-error" role="alert">{{ formErrors().live.transcoding.profile }}</div>
</div> </div>
</ng-container> </ng-container>

View File

@ -1,5 +1,5 @@
import { SelectOptionsItem } from 'src/types/select-options-item.model' import { SelectOptionsItem } from 'src/types/select-options-item.model'
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core' import { Component, OnChanges, OnInit, SimpleChanges, inject, input } from '@angular/core'
import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'
import { HTMLServerConfig } from '@peertube/peertube-models' import { HTMLServerConfig } from '@peertube/peertube-models'
import { ConfigService } from '../shared/config.service' import { ConfigService } from '../shared/config.service'
@ -29,9 +29,12 @@ import { PeertubeCheckboxComponent } from '../../../shared/shared-forms/peertube
] ]
}) })
export class EditLiveConfigurationComponent implements OnInit, OnChanges { export class EditLiveConfigurationComponent implements OnInit, OnChanges {
@Input() form: FormGroup private configService = inject(ConfigService)
@Input() formErrors: any private editConfigurationService = inject(EditConfigurationService)
@Input() serverConfig: HTMLServerConfig
readonly form = input<FormGroup>(undefined)
readonly formErrors = input<any>(undefined)
readonly serverConfig = input<HTMLServerConfig>(undefined)
transcodingThreadOptions: SelectOptionsItem[] = [] transcodingThreadOptions: SelectOptionsItem[] = []
transcodingProfiles: SelectOptionsItem[] = [] transcodingProfiles: SelectOptionsItem[] = []
@ -39,11 +42,6 @@ export class EditLiveConfigurationComponent implements OnInit, OnChanges {
liveMaxDurationOptions: SelectOptionsItem[] = [] liveMaxDurationOptions: SelectOptionsItem[] = []
liveResolutions: ResolutionOption[] = [] liveResolutions: ResolutionOption[] = []
constructor (
private configService: ConfigService,
private editConfigurationService: EditConfigurationService
) { }
ngOnInit () { ngOnInit () {
this.transcodingThreadOptions = this.configService.transcodingThreadOptions this.transcodingThreadOptions = this.configService.transcodingThreadOptions
@ -65,7 +63,7 @@ export class EditLiveConfigurationComponent implements OnInit, OnChanges {
} }
buildAvailableTranscodingProfile () { buildAvailableTranscodingProfile () {
const profiles = this.serverConfig.live.transcoding.availableProfiles const profiles = this.serverConfig().live.transcoding.availableProfiles
return profiles.map(p => { return profiles.map(p => {
if (p === 'default') { if (p === 'default') {
@ -81,15 +79,15 @@ export class EditLiveConfigurationComponent implements OnInit, OnChanges {
} }
getLiveRTMPPort () { getLiveRTMPPort () {
return this.serverConfig.live.rtmp.port return this.serverConfig().live.rtmp.port
} }
isLiveEnabled () { isLiveEnabled () {
return this.editConfigurationService.isLiveEnabled(this.form) return this.editConfigurationService.isLiveEnabled(this.form())
} }
isRemoteRunnerLiveEnabled () { isRemoteRunnerLiveEnabled () {
return this.editConfigurationService.isRemoteRunnerLiveEnabled(this.form) return this.editConfigurationService.isRemoteRunnerLiveEnabled(this.form())
} }
getDisabledLiveClass () { getDisabledLiveClass () {
@ -105,10 +103,10 @@ export class EditLiveConfigurationComponent implements OnInit, OnChanges {
} }
isLiveTranscodingEnabled () { isLiveTranscodingEnabled () {
return this.editConfigurationService.isLiveTranscodingEnabled(this.form) return this.editConfigurationService.isLiveTranscodingEnabled(this.form())
} }
getTotalTranscodingThreads () { getTotalTranscodingThreads () {
return this.editConfigurationService.getTotalTranscodingThreads(this.form) return this.editConfigurationService.getTotalTranscodingThreads(this.form())
} }
} }

View File

@ -1,4 +1,4 @@
<ng-container [formGroup]="form"> <ng-container [formGroup]="form()">
<div class="pt-two-cols mt-4"> <div class="pt-two-cols mt-4">
<div class="title-col"></div> <div class="title-col"></div>
@ -151,7 +151,7 @@
<span>FPS</span> <span>FPS</span>
</div> </div>
<div *ngIf="formErrors.transcoding.fps.max" class="form-error" role="alert">{{ formErrors.transcoding.fps.max }}</div> <div *ngIf="formErrors().transcoding.fps.max" class="form-error" role="alert">{{ formErrors().transcoding.fps.max }}</div>
</div> </div>
<div class="form-group" [ngClass]="getTranscodingDisabledClass()"> <div class="form-group" [ngClass]="getTranscodingDisabledClass()">
@ -220,7 +220,7 @@
[clearable]="false" [clearable]="false"
></my-select-custom-value> ></my-select-custom-value>
<div *ngIf="formErrors.transcoding.threads" class="form-error" role="alert">{{ formErrors.transcoding.threads }}</div> <div *ngIf="formErrors().transcoding.threads" class="form-error" role="alert">{{ formErrors().transcoding.threads }}</div>
</div> </div>
<div class="form-group" [ngClass]="getLocalTranscodingDisabledClass()"> <div class="form-group" [ngClass]="getLocalTranscodingDisabledClass()">
@ -232,7 +232,7 @@
<span i18n>jobs in parallel</span> <span i18n>jobs in parallel</span>
</div> </div>
<div *ngIf="formErrors.transcoding.concurrency" class="form-error" role="alert">{{ formErrors.transcoding.concurrency }}</div> <div *ngIf="formErrors().transcoding.concurrency" class="form-error" role="alert">{{ formErrors().transcoding.concurrency }}</div>
</div> </div>
<div class="form-group" [ngClass]="getLocalTranscodingDisabledClass()"> <div class="form-group" [ngClass]="getLocalTranscodingDisabledClass()">
@ -241,7 +241,7 @@
<my-select-options inputId="transcodingProfile" formControlName="profile" [items]="transcodingProfiles"></my-select-options> <my-select-options inputId="transcodingProfile" formControlName="profile" [items]="transcodingProfiles"></my-select-options>
<div *ngIf="formErrors.transcoding.profile" class="form-error" role="alert">{{ formErrors.transcoding.profile }}</div> <div *ngIf="formErrors().transcoding.profile" class="form-error" role="alert">{{ formErrors().transcoding.profile }}</div>
</div> </div>
</ng-container> </ng-container>

View File

@ -1,5 +1,5 @@
import { NgClass, NgFor, NgIf } from '@angular/common' import { NgClass, NgFor, NgIf } from '@angular/common'
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core' import { Component, OnChanges, OnInit, SimpleChanges, inject, input } from '@angular/core'
import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'
import { RouterLink } from '@angular/router' import { RouterLink } from '@angular/router'
import { Notifier } from '@app/core' import { Notifier } from '@app/core'
@ -30,9 +30,13 @@ import { EditConfigurationService, ResolutionOption } from './edit-configuration
] ]
}) })
export class EditVODTranscodingComponent implements OnInit, OnChanges { export class EditVODTranscodingComponent implements OnInit, OnChanges {
@Input() form: FormGroup private configService = inject(ConfigService)
@Input() formErrors: any private editConfigurationService = inject(EditConfigurationService)
@Input() serverConfig: HTMLServerConfig private notifier = inject(Notifier)
readonly form = input<FormGroup>(undefined)
readonly formErrors = input<any>(undefined)
readonly serverConfig = input<HTMLServerConfig>(undefined)
transcodingThreadOptions: SelectOptionsItem[] = [] transcodingThreadOptions: SelectOptionsItem[] = []
transcodingProfiles: SelectOptionsItem[] = [] transcodingProfiles: SelectOptionsItem[] = []
@ -40,12 +44,6 @@ export class EditVODTranscodingComponent implements OnInit, OnChanges {
additionalVideoExtensions = '' additionalVideoExtensions = ''
constructor (
private configService: ConfigService,
private editConfigurationService: EditConfigurationService,
private notifier: Notifier
) { }
ngOnInit () { ngOnInit () {
this.transcodingThreadOptions = this.configService.transcodingThreadOptions this.transcodingThreadOptions = this.configService.transcodingThreadOptions
this.resolutions = this.editConfigurationService.getTranscodingResolutions() this.resolutions = this.editConfigurationService.getTranscodingResolutions()
@ -57,12 +55,12 @@ export class EditVODTranscodingComponent implements OnInit, OnChanges {
if (changes['serverConfig']) { if (changes['serverConfig']) {
this.transcodingProfiles = this.buildAvailableTranscodingProfile() this.transcodingProfiles = this.buildAvailableTranscodingProfile()
this.additionalVideoExtensions = this.serverConfig.video.file.extensions.join(' ') this.additionalVideoExtensions = this.serverConfig().video.file.extensions.join(' ')
} }
} }
buildAvailableTranscodingProfile () { buildAvailableTranscodingProfile () {
const profiles = this.serverConfig.transcoding.availableProfiles const profiles = this.serverConfig().transcoding.availableProfiles
return profiles.map(p => { return profiles.map(p => {
if (p === 'default') { if (p === 'default') {
@ -78,19 +76,19 @@ export class EditVODTranscodingComponent implements OnInit, OnChanges {
} }
isRemoteRunnerVODEnabled () { isRemoteRunnerVODEnabled () {
return this.editConfigurationService.isRemoteRunnerVODEnabled(this.form) return this.editConfigurationService.isRemoteRunnerVODEnabled(this.form())
} }
isTranscodingEnabled () { isTranscodingEnabled () {
return this.editConfigurationService.isTranscodingEnabled(this.form) return this.editConfigurationService.isTranscodingEnabled(this.form())
} }
isHLSEnabled () { isHLSEnabled () {
return this.editConfigurationService.isHLSEnabled(this.form) return this.editConfigurationService.isHLSEnabled(this.form())
} }
isStudioEnabled () { isStudioEnabled () {
return this.editConfigurationService.isStudioEnabled(this.form) return this.editConfigurationService.isStudioEnabled(this.form())
} }
getTranscodingDisabledClass () { getTranscodingDisabledClass () {
@ -110,14 +108,14 @@ export class EditVODTranscodingComponent implements OnInit, OnChanges {
} }
getTotalTranscodingThreads () { getTotalTranscodingThreads () {
return this.editConfigurationService.getTotalTranscodingThreads(this.form) return this.editConfigurationService.getTotalTranscodingThreads(this.form())
} }
private checkTranscodingFields () { private checkTranscodingFields () {
const transcodingControl = this.form.get('transcoding.enabled') const transcodingControl = this.form().get('transcoding.enabled')
const videoStudioControl = this.form.get('videoStudio.enabled') const videoStudioControl = this.form().get('videoStudio.enabled')
const hlsControl = this.form.get('transcoding.hls.enabled') const hlsControl = this.form().get('transcoding.hls.enabled')
const webVideosControl = this.form.get('transcoding.webVideos.enabled') const webVideosControl = this.form().get('transcoding.webVideos.enabled')
webVideosControl.valueChanges webVideosControl.valueChanges
.subscribe(newValue => { .subscribe(newValue => {
@ -125,7 +123,11 @@ export class EditVODTranscodingComponent implements OnInit, OnChanges {
hlsControl.setValue(true) hlsControl.setValue(true)
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
this.notifier.info($localize`Automatically enable HLS transcoding because at least 1 output format must be enabled when transcoding is enabled`, '', 10000) this.notifier.info(
$localize`Automatically enable HLS transcoding because at least 1 output format must be enabled when transcoding is enabled`,
'',
10000
)
} }
}) })
@ -134,8 +136,12 @@ export class EditVODTranscodingComponent implements OnInit, OnChanges {
if (newValue === false && webVideosControl.value === false) { if (newValue === false && webVideosControl.value === false) {
webVideosControl.setValue(true) webVideosControl.setValue(true)
// eslint-disable-next-line max-len this.notifier.info(
this.notifier.info($localize`Automatically enable Web Videos transcoding because at least 1 output format must be enabled when transcoding is enabled`, '', 10000) // eslint-disable-next-line max-len
$localize`Automatically enable Web Videos transcoding because at least 1 output format must be enabled when transcoding is enabled`,
'',
10000
)
} }
}) })

View File

@ -1,6 +1,6 @@
import { catchError } from 'rxjs/operators' import { catchError } from 'rxjs/operators'
import { HttpClient } from '@angular/common/http' import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable, inject } from '@angular/core'
import { RestExtractor } from '@app/core' import { RestExtractor } from '@app/core'
import { CustomConfig } from '@peertube/peertube-models' import { CustomConfig } from '@peertube/peertube-models'
import { SelectOptionsItem } from '../../../../types/select-options-item.model' import { SelectOptionsItem } from '../../../../types/select-options-item.model'
@ -8,16 +8,16 @@ import { environment } from '../../../../environments/environment'
@Injectable() @Injectable()
export class ConfigService { export class ConfigService {
private authHttp = inject(HttpClient)
private restExtractor = inject(RestExtractor)
private static BASE_APPLICATION_URL = environment.apiUrl + '/api/v1/config' private static BASE_APPLICATION_URL = environment.apiUrl + '/api/v1/config'
videoQuotaOptions: SelectOptionsItem[] = [] videoQuotaOptions: SelectOptionsItem[] = []
videoQuotaDailyOptions: SelectOptionsItem[] = [] videoQuotaDailyOptions: SelectOptionsItem[] = []
transcodingThreadOptions: SelectOptionsItem[] = [] transcodingThreadOptions: SelectOptionsItem[] = []
constructor ( constructor () {
private authHttp: HttpClient,
private restExtractor: RestExtractor
) {
this.videoQuotaOptions = [ this.videoQuotaOptions = [
{ id: -1, label: $localize`Unlimited` }, { id: -1, label: $localize`Unlimited` },
{ id: 0, label: $localize`None - no upload possible` }, { id: 0, label: $localize`None - no upload possible` },
@ -60,11 +60,11 @@ export class ConfigService {
getCustomConfig () { getCustomConfig () {
return this.authHttp.get<CustomConfig>(ConfigService.BASE_APPLICATION_URL + '/custom') return this.authHttp.get<CustomConfig>(ConfigService.BASE_APPLICATION_URL + '/custom')
.pipe(catchError(res => this.restExtractor.handleError(res))) .pipe(catchError(res => this.restExtractor.handleError(res)))
} }
updateCustomConfig (data: CustomConfig) { updateCustomConfig (data: CustomConfig) {
return this.authHttp.put<CustomConfig>(ConfigService.BASE_APPLICATION_URL + '/custom', data) return this.authHttp.put<CustomConfig>(ConfigService.BASE_APPLICATION_URL + '/custom', data)
.pipe(catchError(res => this.restExtractor.handleError(res))) .pipe(catchError(res => this.restExtractor.handleError(res)))
} }
} }

View File

@ -1,5 +1,5 @@
import { NgIf } from '@angular/common' import { NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core' import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
import { formatICU } from '@app/helpers' import { formatICU } from '@app/helpers'
import { InstanceFollowService } from '@app/shared/shared-instance/instance-follow.service' import { InstanceFollowService } from '@app/shared/shared-instance/instance-follow.service'
@ -33,7 +33,11 @@ import { AutoColspanDirective } from '../../../shared/shared-main/common/auto-co
PTDatePipe PTDatePipe
] ]
}) })
export class FollowersListComponent extends RestTable <ActorFollow> implements OnInit { export class FollowersListComponent extends RestTable<ActorFollow> implements OnInit {
private confirmService = inject(ConfirmService)
private notifier = inject(Notifier)
private followService = inject(InstanceFollowService)
followers: ActorFollow[] = [] followers: ActorFollow[] = []
totalRecords = 0 totalRecords = 0
sort: SortMeta = { field: 'createdAt', order: -1 } sort: SortMeta = { field: 'createdAt', order: -1 }
@ -43,14 +47,6 @@ export class FollowersListComponent extends RestTable <ActorFollow> implements O
bulkActions: DropdownAction<ActorFollow[]>[] = [] bulkActions: DropdownAction<ActorFollow[]>[] = []
constructor (
private confirmService: ConfirmService,
private notifier: Notifier,
private followService: InstanceFollowService
) {
super()
}
ngOnInit () { ngOnInit () {
this.initialize() this.initialize()
@ -108,20 +104,20 @@ export class FollowersListComponent extends RestTable <ActorFollow> implements O
if (res === false) return if (res === false) return
this.followService.rejectFollower(follows) this.followService.rejectFollower(follows)
.subscribe({ .subscribe({
next: () => { next: () => {
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
const message = formatICU( const message = formatICU(
$localize`Rejected {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`, $localize`Rejected {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`,
{ count: follows.length, followerName: this.buildFollowerName(follows[0]) } { count: follows.length, followerName: this.buildFollowerName(follows[0]) }
) )
this.notifier.success(message) this.notifier.success(message)
this.reloadData() this.reloadData()
}, },
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
async deleteFollowers (follows: ActorFollow[]) { async deleteFollowers (follows: ActorFollow[]) {
@ -140,21 +136,21 @@ export class FollowersListComponent extends RestTable <ActorFollow> implements O
if (res === false) return if (res === false) return
this.followService.removeFollower(follows) this.followService.removeFollower(follows)
.subscribe({ .subscribe({
next: () => { next: () => {
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
const message = formatICU( const message = formatICU(
$localize`Removed {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`, $localize`Removed {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`,
icuParams icuParams
) )
this.notifier.success(message) this.notifier.success(message)
this.reloadData() this.reloadData()
}, },
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
buildFollowerName (follow: ActorFollow) { buildFollowerName (follow: ActorFollow) {
@ -163,13 +159,13 @@ export class FollowersListComponent extends RestTable <ActorFollow> implements O
protected reloadDataInternal () { protected reloadDataInternal () {
this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search }) this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search })
.subscribe({ .subscribe({
next: resultList => { next: resultList => {
this.followers = resultList.data this.followers = resultList.data
this.totalRecords = resultList.total this.totalRecords = resultList.total
}, },
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
} }

View File

@ -1,5 +1,5 @@
import { NgClass, NgIf } from '@angular/common' import { NgClass, NgIf } from '@angular/common'
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' import { Component, OnInit, inject, output, viewChild } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { Notifier } from '@app/core' import { Notifier } from '@app/core'
import { formatICU } from '@app/helpers' import { formatICU } from '@app/helpers'
@ -20,23 +20,19 @@ import { GlobalIconComponent } from '../../../shared/shared-icons/global-icon.co
imports: [ GlobalIconComponent, FormsModule, ReactiveFormsModule, NgClass, NgIf, AlertComponent ] imports: [ GlobalIconComponent, FormsModule, ReactiveFormsModule, NgClass, NgIf, AlertComponent ]
}) })
export class FollowModalComponent extends FormReactive implements OnInit { export class FollowModalComponent extends FormReactive implements OnInit {
@ViewChild('modal', { static: true }) modal: NgbModal protected formReactiveService = inject(FormReactiveService)
private modalService = inject(NgbModal)
private followService = inject(InstanceFollowService)
private notifier = inject(Notifier)
@Output() newFollow = new EventEmitter<void>() readonly modal = viewChild<NgbModal>('modal')
readonly newFollow = output()
placeholder = 'example.com\nchocobozzz@example.com\nchocobozzz_channel@example.com' placeholder = 'example.com\nchocobozzz@example.com\nchocobozzz_channel@example.com'
private openedModal: NgbModalRef private openedModal: NgbModalRef
constructor (
protected formReactiveService: FormReactiveService,
private modalService: NgbModal,
private followService: InstanceFollowService,
private notifier: Notifier
) {
super()
}
ngOnInit () { ngOnInit () {
this.buildForm({ this.buildForm({
hostsOrHandles: UNIQUE_HOSTS_OR_HANDLE_VALIDATOR hostsOrHandles: UNIQUE_HOSTS_OR_HANDLE_VALIDATOR
@ -44,7 +40,7 @@ export class FollowModalComponent extends FormReactive implements OnInit {
} }
openModal () { openModal () {
this.openedModal = this.modalService.open(this.modal, { centered: true }) this.openedModal = this.modalService.open(this.modal(), { centered: true })
} }
hide () { hide () {

View File

@ -1,5 +1,5 @@
import { NgIf } from '@angular/common' import { NgIf } from '@angular/common'
import { Component, OnInit, ViewChild } from '@angular/core' import { Component, OnInit, inject, viewChild } from '@angular/core'
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core' import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
import { formatICU } from '@app/helpers' import { formatICU } from '@app/helpers'
import { InstanceFollowService } from '@app/shared/shared-instance/instance-follow.service' import { InstanceFollowService } from '@app/shared/shared-instance/instance-follow.service'
@ -34,8 +34,12 @@ import { FollowModalComponent } from './follow-modal.component'
ButtonComponent ButtonComponent
] ]
}) })
export class FollowingListComponent extends RestTable <ActorFollow> implements OnInit { export class FollowingListComponent extends RestTable<ActorFollow> implements OnInit {
@ViewChild('followModal') followModal: FollowModalComponent private notifier = inject(Notifier)
private confirmService = inject(ConfirmService)
private followService = inject(InstanceFollowService)
readonly followModal = viewChild<FollowModalComponent>('followModal')
following: ActorFollow[] = [] following: ActorFollow[] = []
totalRecords = 0 totalRecords = 0
@ -46,14 +50,6 @@ export class FollowingListComponent extends RestTable <ActorFollow> implements O
bulkActions: DropdownAction<ActorFollow[]>[] = [] bulkActions: DropdownAction<ActorFollow[]>[] = []
constructor (
private notifier: Notifier,
private confirmService: ConfirmService,
private followService: InstanceFollowService
) {
super()
}
ngOnInit () { ngOnInit () {
this.initialize() this.initialize()
@ -72,7 +68,7 @@ export class FollowingListComponent extends RestTable <ActorFollow> implements O
} }
openFollowModal () { openFollowModal () {
this.followModal.openModal() this.followModal().openModal()
} }
isInstanceFollowing (follow: ActorFollow) { isInstanceFollowing (follow: ActorFollow) {
@ -113,13 +109,13 @@ export class FollowingListComponent extends RestTable <ActorFollow> implements O
protected reloadDataInternal () { protected reloadDataInternal () {
this.followService.getFollowing({ pagination: this.pagination, sort: this.sort, search: this.search }) this.followService.getFollowing({ pagination: this.pagination, sort: this.sort, search: this.search })
.subscribe({ .subscribe({
next: resultList => { next: resultList => {
this.following = resultList.data this.following = resultList.data
this.totalRecords = resultList.total this.totalRecords = resultList.total
}, },
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
} }

View File

@ -1,3 +1,3 @@
<my-peertube-checkbox <my-peertube-checkbox
[inputName]="host + '-redundancy-allowed'" [(ngModel)]="redundancyAllowed" (ngModelChange)="updateRedundancyState()" [inputName]="host() + '-redundancy-allowed'" [(ngModel)]="redundancyAllowed" (ngModelChange)="updateRedundancyState()"
></my-peertube-checkbox> ></my-peertube-checkbox>

View File

@ -1,4 +1,4 @@
import { Component, Input } from '@angular/core' import { Component, inject, input } from '@angular/core'
import { Notifier } from '@app/core' import { Notifier } from '@app/core'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'
import { PeertubeCheckboxComponent } from '../../../shared/shared-forms/peertube-checkbox.component' import { PeertubeCheckboxComponent } from '../../../shared/shared-forms/peertube-checkbox.component'
@ -10,24 +10,22 @@ import { RedundancyService } from '@app/shared/shared-main/video/redundancy.serv
imports: [ PeertubeCheckboxComponent, FormsModule ] imports: [ PeertubeCheckboxComponent, FormsModule ]
}) })
export class RedundancyCheckboxComponent { export class RedundancyCheckboxComponent {
@Input() redundancyAllowed: boolean private notifier = inject(Notifier)
@Input() host: string private redundancyService = inject(RedundancyService)
constructor ( readonly redundancyAllowed = input<boolean>(undefined)
private notifier: Notifier, readonly host = input<string>(undefined)
private redundancyService: RedundancyService
) { }
updateRedundancyState () { updateRedundancyState () {
this.redundancyService.updateRedundancy(this.host, this.redundancyAllowed) this.redundancyService.updateRedundancy(this.host(), this.redundancyAllowed())
.subscribe({ .subscribe({
next: () => { next: () => {
const stateLabel = this.redundancyAllowed ? $localize`enabled` : $localize`disabled` const stateLabel = this.redundancyAllowed() ? $localize`enabled` : $localize`disabled`
this.notifier.success($localize`Redundancy for ${this.host} is ${stateLabel}`) this.notifier.success($localize`Redundancy for ${this.host()} is ${stateLabel}`)
}, },
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
} }

View File

@ -1,5 +1,5 @@
import { NgFor, NgIf } from '@angular/common' import { NgFor, NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'
import { ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' import { ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
import { BytesPipe } from '@app/shared/shared-main/common/bytes.pipe' import { BytesPipe } from '@app/shared/shared-main/common/bytes.pipe'
@ -38,6 +38,11 @@ import { VideoRedundancyInformationComponent } from './video-redundancy-informat
] ]
}) })
export class VideoRedundanciesListComponent extends RestTable implements OnInit { export class VideoRedundanciesListComponent extends RestTable implements OnInit {
private notifier = inject(Notifier)
private confirmService = inject(ConfirmService)
private redundancyService = inject(RedundancyService)
private serverService = inject(ServerService)
private static LS_DISPLAY_TYPE = 'video-redundancies-list-display-type' private static LS_DISPLAY_TYPE = 'video-redundancies-list-display-type'
videoRedundancies: VideoRedundancy[] = [] videoRedundancies: VideoRedundancy[] = []
@ -56,12 +61,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
private bytesPipe: BytesPipe private bytesPipe: BytesPipe
constructor ( constructor () {
private notifier: Notifier,
private confirmService: ConfirmService,
private redundancyService: RedundancyService,
private serverService: ServerService
) {
super() super()
this.bytesPipe = new BytesPipe() this.bytesPipe = new BytesPipe()
@ -77,15 +77,15 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
this.initialize() this.initialize()
this.serverService.getServerStats() this.serverService.getServerStats()
.subscribe(res => { .subscribe(res => {
const redundancies = res.videosRedundancy const redundancies = res.videosRedundancy
if (redundancies.length === 0) this.noRedundancies = true if (redundancies.length === 0) this.noRedundancies = true
for (const r of redundancies) { for (const r of redundancies) {
this.buildPieData(r) this.buildPieData(r)
} }
}) })
} }
isDisplayingRemoteVideos () { isDisplayingRemoteVideos () {
@ -184,7 +184,6 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
protected reloadDataInternal () { protected reloadDataInternal () {
@ -197,16 +196,16 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
} }
this.redundancyService.listVideoRedundancies(options) this.redundancyService.listVideoRedundancies(options)
.subscribe({ .subscribe({
next: resultList => { next: resultList => {
this.videoRedundancies = resultList.data this.videoRedundancies = resultList.data
this.totalRecords = resultList.total this.totalRecords = resultList.total
this.dataLoaded = true this.dataLoaded = true
}, },
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
private loadSelectLocalStorage () { private loadSelectLocalStorage () {

View File

@ -1,19 +1,19 @@
<div> <div>
<span class="label">Url</span> <span class="label">Url</span>
<a target="_blank" rel="noopener noreferrer" [href]="redundancyElement.fileUrl">{{ redundancyElement.fileUrl }}</a> <a target="_blank" rel="noopener noreferrer" [href]="redundancyElement().fileUrl">{{ redundancyElement().fileUrl }}</a>
</div> </div>
<div> <div>
<span class="label">Created on</span> <span class="label">Created on</span>
<span>{{ redundancyElement.createdAt | ptDate: 'medium' }}</span> <span>{{ redundancyElement().createdAt | ptDate: 'medium' }}</span>
</div> </div>
<div> <div>
<span class="label">Expires on</span> <span class="label">Expires on</span>
<span>{{ redundancyElement.expiresOn | ptDate: 'medium' }}</span> <span>{{ redundancyElement().expiresOn | ptDate: 'medium' }}</span>
</div> </div>
<div> <div>
<span class="label">Size</span> <span class="label">Size</span>
<span>{{ redundancyElement.size | bytes: 1 }}</span> <span>{{ redundancyElement().size | bytes: 1 }}</span>
</div> </div>

View File

@ -1,4 +1,4 @@
import { Component, Input } from '@angular/core' import { Component, input } from '@angular/core'
import { PTDatePipe } from '@app/shared/shared-main/common/date.pipe' import { PTDatePipe } from '@app/shared/shared-main/common/date.pipe'
import { RedundancyInformation } from '@peertube/peertube-models' import { RedundancyInformation } from '@peertube/peertube-models'
import { BytesPipe } from '../../../shared/shared-main/common/bytes.pipe' import { BytesPipe } from '../../../shared/shared-main/common/bytes.pipe'
@ -10,5 +10,5 @@ import { BytesPipe } from '../../../shared/shared-main/common/bytes.pipe'
imports: [ PTDatePipe, BytesPipe ] imports: [ PTDatePipe, BytesPipe ]
}) })
export class VideoRedundancyInformationComponent { export class VideoRedundancyInformationComponent {
@Input() redundancyElement: RedundancyInformation readonly redundancyElement = input<RedundancyInformation>(undefined)
} }

View File

@ -1,13 +1,11 @@
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { AbuseListTableComponent } from '../../../shared/shared-abuse-list/abuse-list-table.component' import { AbuseListTableComponent } from '../../../shared/shared-abuse-list/abuse-list-table.component'
import { GlobalIconComponent } from '../../../shared/shared-icons/global-icon.component'
@Component({ @Component({
selector: 'my-abuse-list', selector: 'my-abuse-list',
templateUrl: './abuse-list.component.html', templateUrl: './abuse-list.component.html',
styleUrls: [], styleUrls: [],
imports: [ GlobalIconComponent, AbuseListTableComponent ] imports: [ AbuseListTableComponent ]
}) })
export class AbuseListComponent { export class AbuseListComponent {
} }

View File

@ -2,7 +2,7 @@ import { SortMeta } from 'primeng/api'
import { from } from 'rxjs' import { from } from 'rxjs'
import { catchError, concatMap, toArray } from 'rxjs/operators' import { catchError, concatMap, toArray } from 'rxjs/operators'
import { HttpClient, HttpParams } from '@angular/common/http' import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable, inject } from '@angular/core'
import { RestExtractor, RestPagination, RestService } from '@app/core' import { RestExtractor, RestPagination, RestService } from '@app/core'
import { arrayify } from '@peertube/peertube-core-utils' import { arrayify } from '@peertube/peertube-core-utils'
import { ResultList, UserRegistration, UserRegistrationUpdateState } from '@peertube/peertube-models' import { ResultList, UserRegistration, UserRegistrationUpdateState } from '@peertube/peertube-models'
@ -10,13 +10,11 @@ import { environment } from '../../../../environments/environment'
@Injectable() @Injectable()
export class AdminRegistrationService { export class AdminRegistrationService {
private static BASE_REGISTRATION_URL = environment.apiUrl + '/api/v1/users/registrations' private authHttp = inject(HttpClient)
private restExtractor = inject(RestExtractor)
private restService = inject(RestService)
constructor ( private static BASE_REGISTRATION_URL = environment.apiUrl + '/api/v1/users/registrations'
private authHttp: HttpClient,
private restExtractor: RestExtractor,
private restService: RestService
) { }
listRegistrations (options: { listRegistrations (options: {
pagination: RestPagination pagination: RestPagination

View File

@ -1,5 +1,5 @@
import { NgClass, NgIf } from '@angular/common' import { NgClass, NgIf } from '@angular/common'
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' import { Component, OnInit, inject, output, viewChild } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { Notifier, ServerService } from '@app/core' import { Notifier, ServerService } from '@app/core'
import { FormReactive } from '@app/shared/shared-forms/form-reactive' import { FormReactive } from '@app/shared/shared-forms/form-reactive'
@ -19,25 +19,21 @@ import { REGISTRATION_MODERATION_RESPONSE_VALIDATOR } from './process-registrati
imports: [ NgIf, GlobalIconComponent, FormsModule, ReactiveFormsModule, NgClass, PeertubeCheckboxComponent, AlertComponent ] imports: [ NgIf, GlobalIconComponent, FormsModule, ReactiveFormsModule, NgClass, PeertubeCheckboxComponent, AlertComponent ]
}) })
export class ProcessRegistrationModalComponent extends FormReactive implements OnInit { export class ProcessRegistrationModalComponent extends FormReactive implements OnInit {
@ViewChild('modal', { static: true }) modal: NgbModal protected formReactiveService = inject(FormReactiveService)
private server = inject(ServerService)
private modalService = inject(NgbModal)
private notifier = inject(Notifier)
private registrationService = inject(AdminRegistrationService)
@Output() registrationProcessed = new EventEmitter() readonly modal = viewChild<NgbModal>('modal')
readonly registrationProcessed = output()
registration: UserRegistration registration: UserRegistration
private openedModal: NgbModalRef private openedModal: NgbModalRef
private processMode: 'accept' | 'reject' private processMode: 'accept' | 'reject'
constructor (
protected formReactiveService: FormReactiveService,
private server: ServerService,
private modalService: NgbModal,
private notifier: Notifier,
private registrationService: AdminRegistrationService
) {
super()
}
ngOnInit () { ngOnInit () {
this.buildForm({ this.buildForm({
moderationResponse: REGISTRATION_MODERATION_RESPONSE_VALIDATOR, moderationResponse: REGISTRATION_MODERATION_RESPONSE_VALIDATOR,
@ -61,7 +57,7 @@ export class ProcessRegistrationModalComponent extends FormReactive implements O
preventEmailDelivery: !this.isEmailEnabled() || registration.emailVerified !== true preventEmailDelivery: !this.isEmailEnabled() || registration.emailVerified !== true
}) })
this.openedModal = this.modalService.open(this.modal, { centered: true }) this.openedModal = this.modalService.open(this.modal(), { centered: true })
} }
hide () { hide () {

View File

@ -1,5 +1,5 @@
import { NgClass, NgIf } from '@angular/common' import { NgClass, NgIf } from '@angular/common'
import { Component, OnInit, ViewChild } from '@angular/core' import { Component, OnInit, inject, viewChild } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
import { formatICU } from '@app/helpers' import { formatICU } from '@app/helpers'
@ -37,8 +37,16 @@ import { ProcessRegistrationModalComponent } from './process-registration-modal.
PTDatePipe PTDatePipe
] ]
}) })
export class RegistrationListComponent extends RestTable <UserRegistration> implements OnInit { export class RegistrationListComponent extends RestTable<UserRegistration> implements OnInit {
@ViewChild('processRegistrationModal', { static: true }) processRegistrationModal: ProcessRegistrationModalComponent protected route = inject(ActivatedRoute)
protected router = inject(Router)
private server = inject(ServerService)
private notifier = inject(Notifier)
private markdownRenderer = inject(MarkdownService)
private confirmService = inject(ConfirmService)
private adminRegistrationService = inject(AdminRegistrationService)
readonly processRegistrationModal = viewChild<ProcessRegistrationModalComponent>('processRegistrationModal')
registrations: (UserRegistration & { registrationReasonHTML?: string, moderationResponseHTML?: string })[] = [] registrations: (UserRegistration & { registrationReasonHTML?: string, moderationResponseHTML?: string })[] = []
totalRecords = 0 totalRecords = 0
@ -52,15 +60,7 @@ export class RegistrationListComponent extends RestTable <UserRegistration> impl
requiresEmailVerification: boolean requiresEmailVerification: boolean
constructor ( constructor () {
protected route: ActivatedRoute,
protected router: Router,
private server: ServerService,
private notifier: Notifier,
private markdownRenderer: MarkdownService,
private confirmService: ConfirmService,
private adminRegistrationService: AdminRegistrationService
) {
super() super()
this.registrationActions = [ this.registrationActions = [
@ -137,7 +137,7 @@ export class RegistrationListComponent extends RestTable <UserRegistration> impl
} }
private openRegistrationRequestProcessModal (registration: UserRegistration, mode: 'accept' | 'reject') { private openRegistrationRequestProcessModal (registration: UserRegistration, mode: 'accept' | 'reject') {
this.processRegistrationModal.openModal(registration, mode) this.processRegistrationModal().openModal(registration, mode)
} }
private async removeRegistrations (registrations: UserRegistration[]) { private async removeRegistrations (registrations: UserRegistration[]) {

View File

@ -1,5 +1,5 @@
import { NgClass, NgIf } from '@angular/common' import { NgClass, NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
import { PTDatePipe } from '@app/shared/shared-main/common/date.pipe' import { PTDatePipe } from '@app/shared/shared-main/common/date.pipe'
@ -42,6 +42,15 @@ import { VideoCellComponent } from '../../../shared/shared-tables/video-cell.com
] ]
}) })
export class VideoBlockListComponent extends RestTable implements OnInit { export class VideoBlockListComponent extends RestTable implements OnInit {
protected route = inject(ActivatedRoute)
protected router = inject(Router)
private notifier = inject(Notifier)
private serverService = inject(ServerService)
private confirmService = inject(ConfirmService)
private videoBlocklistService = inject(VideoBlockService)
private markdownRenderer = inject(MarkdownService)
private videoService = inject(VideoService)
blocklist: (VideoBlacklist & { reasonHtml?: string })[] = [] blocklist: (VideoBlacklist & { reasonHtml?: string })[] = []
totalRecords = 0 totalRecords = 0
sort: SortMeta = { field: 'createdAt', order: -1 } sort: SortMeta = { field: 'createdAt', order: -1 }
@ -66,16 +75,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit {
} }
] ]
constructor ( constructor () {
protected route: ActivatedRoute,
protected router: Router,
private notifier: Notifier,
private serverService: ServerService,
private confirmService: ConfirmService,
private videoBlocklistService: VideoBlockService,
private markdownRenderer: MarkdownService,
private videoService: VideoService
) {
super() super()
this.videoBlocklistActions = [ this.videoBlocklistActions = [

View File

@ -1,12 +1,11 @@
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { GlobalIconComponent } from '@app/shared/shared-icons/global-icon.component'
import { WatchedWordsListAdminOwnerComponent } from '@app/shared/standalone-watched-words/watched-words-list-admin-owner.component' import { WatchedWordsListAdminOwnerComponent } from '@app/shared/standalone-watched-words/watched-words-list-admin-owner.component'
@Component({ @Component({
templateUrl: './watched-words-list-admin.component.html', templateUrl: './watched-words-list-admin.component.html',
imports: [ imports: [
GlobalIconComponent,
WatchedWordsListAdminOwnerComponent WatchedWordsListAdminOwnerComponent
] ]
}) })
export class WatchedWordsListAdminComponent { } export class WatchedWordsListAdminComponent {}

View File

@ -1,16 +1,13 @@
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { VideoCommentService } from '@app/shared/shared-video-comment/video-comment.service' import { VideoCommentService } from '@app/shared/shared-video-comment/video-comment.service'
import { FeedFormat } from '@peertube/peertube-models' import { FeedFormat } from '@peertube/peertube-models'
import { GlobalIconComponent } from '../../../shared/shared-icons/global-icon.component'
import { FeedComponent } from '../../../shared/shared-main/feeds/feed.component'
import { VideoCommentListAdminOwnerComponent } from '../../../shared/shared-video-comment/video-comment-list-admin-owner.component' import { VideoCommentListAdminOwnerComponent } from '../../../shared/shared-video-comment/video-comment-list-admin-owner.component'
@Component({ @Component({
selector: 'my-video-comment-list', selector: 'my-video-comment-list',
templateUrl: './video-comment-list.component.html', templateUrl: './video-comment-list.component.html',
imports: [ imports: [
GlobalIconComponent,
FeedComponent,
VideoCommentListAdminOwnerComponent VideoCommentListAdminOwnerComponent
] ]
}) })

View File

@ -1,5 +1,5 @@
import { NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common' import { NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { Router, RouterLink } from '@angular/router' import { Router, RouterLink } from '@angular/router'
import { ConfigService } from '@app/+admin/config/shared/config.service' import { ConfigService } from '@app/+admin/config/shared/config.service'
@ -54,18 +54,18 @@ import { UserPasswordComponent } from './user-password.component'
] ]
}) })
export class UserCreateComponent extends UserEdit implements OnInit { export class UserCreateComponent extends UserEdit implements OnInit {
protected serverService = inject(ServerService)
protected formReactiveService = inject(FormReactiveService)
protected configService = inject(ConfigService)
protected screenService = inject(ScreenService)
protected auth = inject(AuthService)
private router = inject(Router)
private notifier = inject(Notifier)
private userAdminService = inject(UserAdminService)
error: string error: string
constructor ( constructor () {
protected serverService: ServerService,
protected formReactiveService: FormReactiveService,
protected configService: ConfigService,
protected screenService: ScreenService,
protected auth: AuthService,
private router: Router,
private notifier: Notifier,
private userAdminService: UserAdminService
) {
super() super()
this.buildQuotaOptions() this.buildQuotaOptions()

View File

@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core' import { Component, OnInit, inject, input } from '@angular/core'
import { Notifier } from '@app/core' import { Notifier } from '@app/core'
import { USER_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators' import { USER_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators'
import { FormReactive } from '@app/shared/shared-forms/form-reactive' import { FormReactive } from '@app/shared/shared-forms/form-reactive'
@ -15,20 +15,16 @@ import { UserAdminService } from '@app/shared/shared-users/user-admin.service'
imports: [ FormsModule, ReactiveFormsModule, NgClass, NgIf ] imports: [ FormsModule, ReactiveFormsModule, NgClass, NgIf ]
}) })
export class UserPasswordComponent extends FormReactive implements OnInit { export class UserPasswordComponent extends FormReactive implements OnInit {
@Input() userId: number protected formReactiveService = inject(FormReactiveService)
@Input() username: string private notifier = inject(Notifier)
private userAdminService = inject(UserAdminService)
readonly userId = input<number>(undefined)
readonly username = input<string>(undefined)
error: string error: string
showPassword = false showPassword = false
constructor (
protected formReactiveService: FormReactiveService,
private notifier: Notifier,
private userAdminService: UserAdminService
) {
super()
}
ngOnInit () { ngOnInit () {
this.buildForm({ this.buildForm({
password: USER_PASSWORD_VALIDATOR password: USER_PASSWORD_VALIDATOR
@ -40,9 +36,9 @@ export class UserPasswordComponent extends FormReactive implements OnInit {
const userUpdate: UserUpdate = this.form.value const userUpdate: UserUpdate = this.form.value
this.userAdminService.updateUser(this.userId, userUpdate) this.userAdminService.updateUser(this.userId(), userUpdate)
.subscribe({ .subscribe({
next: () => this.notifier.success($localize`Password changed for user ${this.username}.`), next: () => this.notifier.success($localize`Password changed for user ${this.username()}.`),
error: err => { error: err => {
this.error = err.message this.error = err.message

View File

@ -1,5 +1,5 @@
import { NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common' import { NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common'
import { Component, OnDestroy, OnInit } from '@angular/core' import { Component, OnDestroy, OnInit, inject } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { ActivatedRoute, Router, RouterLink } from '@angular/router' import { ActivatedRoute, Router, RouterLink } from '@angular/router'
import { ConfigService } from '@app/+admin/config/shared/config.service' import { ConfigService } from '@app/+admin/config/shared/config.service'
@ -52,23 +52,23 @@ import { UserPasswordComponent } from './user-password.component'
] ]
}) })
export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy { export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
protected formReactiveService = inject(FormReactiveService)
protected serverService = inject(ServerService)
protected configService = inject(ConfigService)
protected screenService = inject(ScreenService)
protected auth = inject(AuthService)
private route = inject(ActivatedRoute)
private router = inject(Router)
private notifier = inject(Notifier)
private userService = inject(UserService)
private twoFactorService = inject(TwoFactorService)
private userAdminService = inject(UserAdminService)
error: string error: string
private paramsSub: Subscription private paramsSub: Subscription
constructor ( constructor () {
protected formReactiveService: FormReactiveService,
protected serverService: ServerService,
protected configService: ConfigService,
protected screenService: ScreenService,
protected auth: AuthService,
private route: ActivatedRoute,
private router: Router,
private notifier: Notifier,
private userService: UserService,
private twoFactorService: TwoFactorService,
private userAdminService: UserAdminService
) {
super() super()
this.buildQuotaOptions() this.buildQuotaOptions()
@ -168,7 +168,6 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
private onUserFetched (userJson: UserType) { private onUserFetched (userJson: UserType) {

View File

@ -1,5 +1,5 @@
import { NgClass, NgIf } from '@angular/common' import { NgClass, NgIf } from '@angular/common'
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core' import { Component, OnDestroy, OnInit, inject, viewChild } from '@angular/core'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'
import { ActivatedRoute, Router, RouterLink } from '@angular/router' import { ActivatedRoute, Router, RouterLink } from '@angular/router'
import { import {
@ -79,10 +79,21 @@ type UserForList = User & {
ProgressBarComponent ProgressBarComponent
] ]
}) })
export class UserListComponent extends RestTable <User> implements OnInit, OnDestroy { export class UserListComponent extends RestTable<User> implements OnInit, OnDestroy {
protected route = inject(ActivatedRoute)
protected router = inject(Router)
private notifier = inject(Notifier)
private confirmService = inject(ConfirmService)
private auth = inject(AuthService)
private blocklist = inject(BlocklistService)
private userAdminService = inject(UserAdminService)
private peertubeLocalStorage = inject(LocalStorageService)
private hooks = inject(HooksService)
private pluginService = inject(PluginService)
private static readonly LS_SELECTED_COLUMNS_KEY = 'admin-user-list-selected-columns' private static readonly LS_SELECTED_COLUMNS_KEY = 'admin-user-list-selected-columns'
@ViewChild('userBanModal', { static: true }) userBanModal: UserBanModalComponent readonly userBanModal = viewChild<UserBanModalComponent>('userBanModal')
users: (User & { accountMutedStatus: AccountMutedStatus })[] = [] users: (User & { accountMutedStatus: AccountMutedStatus })[] = []
@ -115,21 +126,6 @@ export class UserListComponent extends RestTable <User> implements OnInit, OnDes
private _selectedColumns: string[] = [] private _selectedColumns: string[] = []
constructor (
protected route: ActivatedRoute,
protected router: Router,
private notifier: Notifier,
private confirmService: ConfirmService,
private auth: AuthService,
private blocklist: BlocklistService,
private userAdminService: UserAdminService,
private peertubeLocalStorage: LocalStorageService,
private hooks: HooksService,
private pluginService: PluginService
) {
super()
}
get authUser () { get authUser () {
return this.auth.getUser() return this.auth.getUser()
} }
@ -262,7 +258,7 @@ export class UserListComponent extends RestTable <User> implements OnInit, OnDes
} }
} }
this.userBanModal.openModal(users) this.userBanModal().openModal(users)
} }
onUserChanged () { onUserChanged () {
@ -281,19 +277,19 @@ export class UserListComponent extends RestTable <User> implements OnInit, OnDes
if (res === false) return if (res === false) return
this.userAdminService.unbanUsers(users) this.userAdminService.unbanUsers(users)
.subscribe({ .subscribe({
next: () => { next: () => {
this.notifier.success( this.notifier.success(
formatICU( formatICU(
$localize`{count, plural, =1 {1 user unbanned.} other {{count} users unbanned.}}`, $localize`{count, plural, =1 {1 user unbanned.} other {{count} users unbanned.}}`,
{ count: users.length } { count: users.length }
)
) )
this.reloadData() )
}, this.reloadData()
},
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
async removeUsers (users: User[]) { async removeUsers (users: User[]) {

View File

@ -1,5 +1,5 @@
import { HttpClient, HttpParams } from '@angular/common/http' import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable, inject } from '@angular/core'
import { RestExtractor, RestPagination, RestService } from '@app/core' import { RestExtractor, RestPagination, RestService } from '@app/core'
import { AdvancedInputFilter } from '@app/shared/shared-forms/advanced-input-filter.component' import { AdvancedInputFilter } from '@app/shared/shared-forms/advanced-input-filter.component'
import { Video } from '@app/shared/shared-main/video/video.model' import { Video } from '@app/shared/shared-main/video/video.model'
@ -11,13 +11,10 @@ import { catchError, switchMap } from 'rxjs/operators'
@Injectable() @Injectable()
export class VideoAdminService { export class VideoAdminService {
private videoService = inject(VideoService)
constructor ( private authHttp = inject(HttpClient)
private videoService: VideoService, private restExtractor = inject(RestExtractor)
private authHttp: HttpClient, private restService = inject(RestService)
private restExtractor: RestExtractor,
private restService: RestService
) {}
getAdminVideos ( getAdminVideos (
options: CommonVideoParams & { pagination: RestPagination, search?: string } options: CommonVideoParams & { pagination: RestPagination, search?: string }
@ -28,16 +25,16 @@ export class VideoAdminService {
params = this.videoService.buildCommonVideosParams({ params, ...omit(options, [ 'search', 'pagination' ]) }) params = this.videoService.buildCommonVideosParams({ params, ...omit(options, [ 'search', 'pagination' ]) })
params = params.set('start', pagination.start.toString()) params = params.set('start', pagination.start.toString())
.set('count', pagination.count.toString()) .set('count', pagination.count.toString())
params = this.buildAdminParamsFromSearch(search, params) params = this.buildAdminParamsFromSearch(search, params)
return this.authHttp return this.authHttp
.get<ResultList<Video>>(VideoService.BASE_VIDEO_URL, { params }) .get<ResultList<Video>>(VideoService.BASE_VIDEO_URL, { params })
.pipe( .pipe(
switchMap(res => this.videoService.extractVideos(res)), switchMap(res => this.videoService.extractVideos(res)),
catchError(err => this.restExtractor.handleError(err)) catchError(err => this.restExtractor.handleError(err))
) )
} }
buildAdminInputFilter (): AdvancedInputFilter[] { buildAdminInputFilter (): AdvancedInputFilter[] {

View File

@ -1,5 +1,5 @@
import { NgClass, NgFor, NgIf } from '@angular/common' import { NgClass, NgFor, NgIf } from '@angular/common'
import { Component, OnInit, ViewChild } from '@angular/core' import { Component, OnInit, inject, viewChild } from '@angular/core'
import { ActivatedRoute, Router, RouterLink } from '@angular/router' import { ActivatedRoute, Router, RouterLink } from '@angular/router'
import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
import { formatICU } from '@app/helpers' import { formatICU } from '@app/helpers'
@ -59,8 +59,20 @@ import { VideoAdminService } from './video-admin.service'
BytesPipe BytesPipe
] ]
}) })
export class VideoListComponent extends RestTable <Video> implements OnInit { export class VideoListComponent extends RestTable<Video> implements OnInit {
@ViewChild('videoBlockModal') videoBlockModal: VideoBlockComponent protected route = inject(ActivatedRoute)
protected router = inject(Router)
private confirmService = inject(ConfirmService)
private auth = inject(AuthService)
private notifier = inject(Notifier)
private videoService = inject(VideoService)
private videoAdminService = inject(VideoAdminService)
private videoBlockService = inject(VideoBlockService)
private videoCaptionService = inject(VideoCaptionService)
private server = inject(ServerService)
private videoFileTokenService = inject(VideoFileTokenService)
readonly videoBlockModal = viewChild<VideoBlockComponent>('videoBlockModal')
videos: Video[] = [] videos: Video[] = []
@ -91,23 +103,7 @@ export class VideoListComponent extends RestTable <Video> implements OnInit {
loading = true loading = true
private videoFileTokens: { [ videoId: number ]: string } = {} private videoFileTokens: { [videoId: number]: string } = {}
constructor (
protected route: ActivatedRoute,
protected router: Router,
private confirmService: ConfirmService,
private auth: AuthService,
private notifier: Notifier,
private videoService: VideoService,
private videoAdminService: VideoAdminService,
private videoBlockService: VideoBlockService,
private videoCaptionService: VideoCaptionService,
private server: ServerService,
private videoFileTokenService: VideoFileTokenService
) {
super()
}
get authUser () { get authUser () {
return this.auth.getUser() return this.auth.getUser()
@ -132,7 +128,7 @@ export class VideoListComponent extends RestTable <Video> implements OnInit {
}, },
{ {
label: $localize`Block`, label: $localize`Block`,
handler: videos => this.videoBlockModal.show(videos), handler: videos => this.videoBlockModal().show(videos),
isDisplayed: videos => this.authUser.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) && videos.every(v => !v.blacklisted), isDisplayed: videos => this.authUser.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) && videos.every(v => !v.blacklisted),
iconName: 'no' iconName: 'no'
}, },

View File

@ -1,5 +1,5 @@
import { NgFor, NgIf } from '@angular/common' import { NgFor, NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service' import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
import { ComponentPagination, ConfirmService, hasMoreItems, Notifier, resetCurrentPage, updatePaginationOnDelete } from '@app/core' import { ComponentPagination, ConfirmService, hasMoreItems, Notifier, resetCurrentPage, updatePaginationOnDelete } from '@app/core'
@ -26,6 +26,13 @@ import { PluginCardComponent } from '../shared/plugin-card.component'
] ]
}) })
export class PluginListInstalledComponent implements OnInit { export class PluginListInstalledComponent implements OnInit {
private pluginService = inject(PluginService)
private pluginApiService = inject(PluginApiService)
private notifier = inject(Notifier)
private confirmService = inject(ConfirmService)
private router = inject(Router)
private route = inject(ActivatedRoute)
pluginType: PluginType_Type pluginType: PluginType_Type
pagination: ComponentPagination = { pagination: ComponentPagination = {
@ -41,16 +48,6 @@ export class PluginListInstalledComponent implements OnInit {
onDataSubject = new Subject<any[]>() onDataSubject = new Subject<any[]>()
constructor (
private pluginService: PluginService,
private pluginApiService: PluginApiService,
private notifier: Notifier,
private confirmService: ConfirmService,
private router: Router,
private route: ActivatedRoute
) {
}
ngOnInit () { ngOnInit () {
if (!this.route.snapshot.queryParams['pluginType']) { if (!this.route.snapshot.queryParams['pluginType']) {
const queryParams = { pluginType: PluginType.PLUGIN } const queryParams = { pluginType: PluginType.PLUGIN }
@ -76,16 +73,16 @@ export class PluginListInstalledComponent implements OnInit {
loadMorePlugins () { loadMorePlugins () {
this.pluginApiService.getPlugins(this.pluginType, this.pagination, this.sort) this.pluginApiService.getPlugins(this.pluginType, this.pagination, this.sort)
.subscribe({ .subscribe({
next: res => { next: res => {
this.plugins = this.plugins.concat(res.data) this.plugins = this.plugins.concat(res.data)
this.pagination.totalItems = res.total this.pagination.totalItems = res.total
this.onDataSubject.next(res.data) this.onDataSubject.next(res.data)
}, },
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
onNearOfBottom () { onNearOfBottom () {
@ -171,21 +168,21 @@ export class PluginListInstalledComponent implements OnInit {
this.updating[pluginKey] = true this.updating[pluginKey] = true
this.pluginApiService.update(plugin.name, plugin.type) this.pluginApiService.update(plugin.name, plugin.type)
.pipe() .pipe()
.subscribe({ .subscribe({
next: res => { next: res => {
this.updating[pluginKey] = false this.updating[pluginKey] = false
this.notifier.success($localize`${plugin.name} updated.`) this.notifier.success($localize`${plugin.name} updated.`)
Object.assign(plugin, res) Object.assign(plugin, res)
}, },
error: err => { error: err => {
this.notifier.error(err.message) this.notifier.error(err.message)
this.updating[pluginKey] = false this.updating[pluginKey] = false
} }
}) })
} }
getShowRouterLink (plugin: PeerTubePlugin) { getShowRouterLink (plugin: PeerTubePlugin) {

View File

@ -1,5 +1,5 @@
import { NgFor, NgIf } from '@angular/common' import { NgFor, NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service' import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
import { ComponentPagination, ConfirmService, hasMoreItems, Notifier, PluginService, resetCurrentPage } from '@app/core' import { ComponentPagination, ConfirmService, hasMoreItems, Notifier, PluginService, resetCurrentPage } from '@app/core'
@ -32,6 +32,13 @@ import { PluginCardComponent } from '../shared/plugin-card.component'
] ]
}) })
export class PluginSearchComponent implements OnInit { export class PluginSearchComponent implements OnInit {
private pluginService = inject(PluginService)
private pluginApiService = inject(PluginApiService)
private notifier = inject(Notifier)
private confirmService = inject(ConfirmService)
private router = inject(Router)
private route = inject(ActivatedRoute)
pluginType: PluginType_Type pluginType: PluginType_Type
pagination: ComponentPagination = { pagination: ComponentPagination = {
@ -52,16 +59,6 @@ export class PluginSearchComponent implements OnInit {
private searchSubject = new Subject<string>() private searchSubject = new Subject<string>()
constructor (
private pluginService: PluginService,
private pluginApiService: PluginApiService,
private notifier: Notifier,
private confirmService: ConfirmService,
private router: Router,
private route: ActivatedRoute
) {
}
ngOnInit () { ngOnInit () {
if (!this.route.snapshot.queryParams['pluginType']) { if (!this.route.snapshot.queryParams['pluginType']) {
const queryParams = { pluginType: PluginType.PLUGIN } const queryParams = { pluginType: PluginType.PLUGIN }
@ -79,11 +76,11 @@ export class PluginSearchComponent implements OnInit {
}) })
this.searchSubject.asObservable() this.searchSubject.asObservable()
.pipe( .pipe(
debounceTime(400), debounceTime(400),
distinctUntilChanged() distinctUntilChanged()
) )
.subscribe(search => this.router.navigate([], { queryParams: { search }, queryParamsHandling: 'merge' })) .subscribe(search => this.router.navigate([], { queryParams: { search }, queryParamsHandling: 'merge' }))
} }
onSearchChange (event: Event) { onSearchChange (event: Event) {
@ -103,23 +100,23 @@ export class PluginSearchComponent implements OnInit {
this.isSearching = true this.isSearching = true
this.pluginApiService.searchAvailablePlugins(this.pluginType, this.pagination, this.sort, this.search) this.pluginApiService.searchAvailablePlugins(this.pluginType, this.pagination, this.sort, this.search)
.subscribe({ .subscribe({
next: res => { next: res => {
this.isSearching = false this.isSearching = false
this.plugins = this.plugins.concat(res.data) this.plugins = this.plugins.concat(res.data)
this.pagination.totalItems = res.total this.pagination.totalItems = res.total
this.onDataSubject.next(res.data) this.onDataSubject.next(res.data)
}, },
error: err => { error: err => {
logger.error(err) logger.error(err)
const message = $localize`The plugin index is not available. Please retry later.` const message = $localize`The plugin index is not available. Please retry later.`
this.notifier.error(message) this.notifier.error(message)
} }
}) })
} }
onNearOfBottom () { onNearOfBottom () {
@ -154,21 +151,21 @@ export class PluginSearchComponent implements OnInit {
this.installing[plugin.npmName] = true this.installing[plugin.npmName] = true
this.pluginApiService.install(plugin.npmName) this.pluginApiService.install(plugin.npmName)
.subscribe({ .subscribe({
next: () => { next: () => {
this.installing[plugin.npmName] = false this.installing[plugin.npmName] = false
this.pluginInstalled = true this.pluginInstalled = true
this.notifier.success($localize`${plugin.name} installed.`) this.notifier.success($localize`${plugin.name} installed.`)
plugin.installed = true plugin.installed = true
}, },
error: err => { error: err => {
this.installing[plugin.npmName] = false this.installing[plugin.npmName] = false
this.notifier.error(err.message) this.notifier.error(err.message)
} }
}) })
} }
} }

View File

@ -1,6 +1,6 @@
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { map, switchMap } from 'rxjs/operators' import { map, switchMap } from 'rxjs/operators'
import { Component, OnDestroy, OnInit } from '@angular/core' import { Component, OnDestroy, OnInit, inject } from '@angular/core'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { HooksService, Notifier, PluginService } from '@app/core' import { HooksService, Notifier, PluginService } from '@app/core'
import { FormReactive } from '@app/shared/shared-forms/form-reactive' import { FormReactive } from '@app/shared/shared-forms/form-reactive'
@ -18,6 +18,13 @@ import { BuildFormArgument } from '@app/shared/form-validators/form-validator.mo
imports: [ NgIf, FormsModule, ReactiveFormsModule, NgFor, DynamicFormFieldComponent ] imports: [ NgIf, FormsModule, ReactiveFormsModule, NgFor, DynamicFormFieldComponent ]
}) })
export class PluginShowInstalledComponent extends FormReactive implements OnInit, OnDestroy { export class PluginShowInstalledComponent extends FormReactive implements OnInit, OnDestroy {
protected formReactiveService = inject(FormReactiveService)
private pluginService = inject(PluginService)
private pluginAPIService = inject(PluginApiService)
private notifier = inject(Notifier)
private hooks = inject(HooksService)
private route = inject(ActivatedRoute)
plugin: PeerTubePlugin plugin: PeerTubePlugin
registeredSettings: RegisterServerSettingOptions[] = [] registeredSettings: RegisterServerSettingOptions[] = []
pluginTypeLabel: string pluginTypeLabel: string
@ -25,17 +32,6 @@ export class PluginShowInstalledComponent extends FormReactive implements OnInit
private sub: Subscription private sub: Subscription
private npmName: string private npmName: string
constructor (
protected formReactiveService: FormReactiveService,
private pluginService: PluginService,
private pluginAPIService: PluginApiService,
private notifier: Notifier,
private hooks: HooksService,
private route: ActivatedRoute
) {
super()
}
ngOnInit () { ngOnInit () {
this.sub = this.route.params.subscribe( this.sub = this.route.params.subscribe(
routeParams => { routeParams => {
@ -54,13 +50,13 @@ export class PluginShowInstalledComponent extends FormReactive implements OnInit
const settings = this.form.value const settings = this.form.value
this.pluginAPIService.updatePluginSettings(this.plugin.name, this.plugin.type, settings) this.pluginAPIService.updatePluginSettings(this.plugin.name, this.plugin.type, settings)
.subscribe({ .subscribe({
next: () => { next: () => {
this.notifier.success($localize`Settings updated.`) this.notifier.success($localize`Settings updated.`)
}, },
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
hasRegisteredSettings () { hasRegisteredSettings () {
@ -83,23 +79,23 @@ export class PluginShowInstalledComponent extends FormReactive implements OnInit
private loadPlugin (npmName: string) { private loadPlugin (npmName: string) {
this.pluginAPIService.getPlugin(npmName) this.pluginAPIService.getPlugin(npmName)
.pipe(switchMap(plugin => { .pipe(switchMap(plugin => {
return this.pluginAPIService.getPluginRegisteredSettings(plugin.name, plugin.type) return this.pluginAPIService.getPluginRegisteredSettings(plugin.name, plugin.type)
.pipe(map(data => ({ plugin, registeredSettings: data.registeredSettings }))) .pipe(map(data => ({ plugin, registeredSettings: data.registeredSettings })))
})) }))
.subscribe({ .subscribe({
next: async ({ plugin, registeredSettings }) => { next: async ({ plugin, registeredSettings }) => {
this.plugin = plugin this.plugin = plugin
this.registeredSettings = await this.translateSettings(registeredSettings) this.registeredSettings = await this.translateSettings(registeredSettings)
this.pluginTypeLabel = this.pluginAPIService.getPluginTypeLabel(this.plugin.type) this.pluginTypeLabel = this.pluginAPIService.getPluginTypeLabel(this.plugin.type)
this.buildSettingsForm() this.buildSettingsForm()
}, },
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
private buildSettingsForm () { private buildSettingsForm () {

View File

@ -1,6 +1,6 @@
import { catchError } from 'rxjs/operators' import { catchError } from 'rxjs/operators'
import { HttpClient, HttpParams } from '@angular/common/http' import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable, inject } from '@angular/core'
import { ComponentPagination, RestExtractor, RestService } from '@app/core' import { ComponentPagination, RestExtractor, RestService } from '@app/core'
import { PluginService } from '@app/core/plugins/plugin.service' import { PluginService } from '@app/core/plugins/plugin.service'
import { import {
@ -17,14 +17,12 @@ import { environment } from '../../../../environments/environment'
@Injectable() @Injectable()
export class PluginApiService { export class PluginApiService {
private static BASE_PLUGIN_URL = environment.apiUrl + '/api/v1/plugins' private authHttp = inject(HttpClient)
private restExtractor = inject(RestExtractor)
private restService = inject(RestService)
private pluginService = inject(PluginService)
constructor ( private static BASE_PLUGIN_URL = environment.apiUrl + '/api/v1/plugins'
private authHttp: HttpClient,
private restExtractor: RestExtractor,
private restService: RestService,
private pluginService: PluginService
) { }
getPluginTypeLabel (type: PluginType_Type) { getPluginTypeLabel (type: PluginType_Type) {
if (type === PluginType.PLUGIN) { if (type === PluginType.PLUGIN) {
@ -46,7 +44,7 @@ export class PluginApiService {
params = params.append('pluginType', pluginType.toString()) params = params.append('pluginType', pluginType.toString())
return this.authHttp.get<ResultList<PeerTubePlugin>>(PluginApiService.BASE_PLUGIN_URL, { params }) return this.authHttp.get<ResultList<PeerTubePlugin>>(PluginApiService.BASE_PLUGIN_URL, { params })
.pipe(catchError(res => this.restExtractor.handleError(res))) .pipe(catchError(res => this.restExtractor.handleError(res)))
} }
searchAvailablePlugins ( searchAvailablePlugins (
@ -64,14 +62,14 @@ export class PluginApiService {
if (search) params = params.append('search', search) if (search) params = params.append('search', search)
return this.authHttp.get<ResultList<PeerTubePluginIndex>>(PluginApiService.BASE_PLUGIN_URL + '/available', { params }) return this.authHttp.get<ResultList<PeerTubePluginIndex>>(PluginApiService.BASE_PLUGIN_URL + '/available', { params })
.pipe(catchError(res => this.restExtractor.handleError(res))) .pipe(catchError(res => this.restExtractor.handleError(res)))
} }
getPlugin (npmName: string) { getPlugin (npmName: string) {
const path = PluginApiService.BASE_PLUGIN_URL + '/' + npmName const path = PluginApiService.BASE_PLUGIN_URL + '/' + npmName
return this.authHttp.get<PeerTubePlugin>(path) return this.authHttp.get<PeerTubePlugin>(path)
.pipe(catchError(res => this.restExtractor.handleError(res))) .pipe(catchError(res => this.restExtractor.handleError(res)))
} }
getPluginRegisteredSettings (pluginName: string, pluginType: PluginType_Type) { getPluginRegisteredSettings (pluginName: string, pluginType: PluginType_Type) {
@ -79,9 +77,9 @@ export class PluginApiService {
const path = PluginApiService.BASE_PLUGIN_URL + '/' + npmName + '/registered-settings' const path = PluginApiService.BASE_PLUGIN_URL + '/' + npmName + '/registered-settings'
return this.authHttp.get<RegisteredServerSettings>(path) return this.authHttp.get<RegisteredServerSettings>(path)
.pipe( .pipe(
catchError(res => this.restExtractor.handleError(res)) catchError(res => this.restExtractor.handleError(res))
) )
} }
updatePluginSettings (pluginName: string, pluginType: PluginType_Type, settings: any) { updatePluginSettings (pluginName: string, pluginType: PluginType_Type, settings: any) {
@ -89,7 +87,7 @@ export class PluginApiService {
const path = PluginApiService.BASE_PLUGIN_URL + '/' + npmName + '/settings' const path = PluginApiService.BASE_PLUGIN_URL + '/' + npmName + '/settings'
return this.authHttp.put(path, { settings }) return this.authHttp.put(path, { settings })
.pipe(catchError(res => this.restExtractor.handleError(res))) .pipe(catchError(res => this.restExtractor.handleError(res)))
} }
uninstall (pluginName: string, pluginType: PluginType_Type) { uninstall (pluginName: string, pluginType: PluginType_Type) {
@ -98,7 +96,7 @@ export class PluginApiService {
} }
return this.authHttp.post(PluginApiService.BASE_PLUGIN_URL + '/uninstall', body) return this.authHttp.post(PluginApiService.BASE_PLUGIN_URL + '/uninstall', body)
.pipe(catchError(res => this.restExtractor.handleError(res))) .pipe(catchError(res => this.restExtractor.handleError(res)))
} }
update (pluginName: string, pluginType: PluginType_Type) { update (pluginName: string, pluginType: PluginType_Type) {
@ -107,7 +105,7 @@ export class PluginApiService {
} }
return this.authHttp.post(PluginApiService.BASE_PLUGIN_URL + '/update', body) return this.authHttp.post(PluginApiService.BASE_PLUGIN_URL + '/update', body)
.pipe(catchError(res => this.restExtractor.handleError(res))) .pipe(catchError(res => this.restExtractor.handleError(res)))
} }
install (npmName: string) { install (npmName: string) {
@ -116,7 +114,7 @@ export class PluginApiService {
} }
return this.authHttp.post(PluginApiService.BASE_PLUGIN_URL + '/install', body) return this.authHttp.post(PluginApiService.BASE_PLUGIN_URL + '/install', body)
.pipe(catchError(res => this.restExtractor.handleError(res))) .pipe(catchError(res => this.restExtractor.handleError(res)))
} }
getPluginOrThemeHref (type: PluginType_Type, name: string) { getPluginOrThemeHref (type: PluginType_Type, name: string) {

View File

@ -1,15 +1,15 @@
<div class="card plugin"> <div class="card plugin">
<div class="card-body"> <div class="card-body">
<div class="first-row"> <div class="first-row">
<span class="plugin-name">{{ plugin.name }}</span> <span class="plugin-name">{{ plugin().name }}</span>
<span class="plugin-version">{{ version }}</span> <span class="plugin-version">{{ version() }}</span>
<a class="plugin-icon" target="_blank" rel="noopener noreferrer" [href]="plugin.homepage" i18n-title title="Homepage (new window)"> <a class="plugin-icon" target="_blank" rel="noopener noreferrer" [href]="plugin().homepage" i18n-title title="Homepage (new window)">
<my-global-icon iconName="home"></my-global-icon> <my-global-icon iconName="home"></my-global-icon>
</a> </a>
<a class="plugin-icon" target="_blank" rel="noopener noreferrer" [href]="getPluginOrThemeHref(plugin.name)" i18n-title title="NPM page (new window)"> <a class="plugin-icon" target="_blank" rel="noopener noreferrer" [href]="getPluginOrThemeHref(plugin().name)" i18n-title title="NPM page (new window)">
<my-global-icon iconName="registry"></my-global-icon> <my-global-icon iconName="registry"></my-global-icon>
</a> </a>
@ -21,7 +21,7 @@
</div> </div>
<div class="second-row"> <div class="second-row">
<div dir="auto" class="description">{{ plugin.description }}</div> <div dir="auto" class="description">{{ plugin().description }}</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,4 +1,4 @@
import { Component, Input } from '@angular/core' import { Component, inject, input } from '@angular/core'
import { PeerTubePlugin, PeerTubePluginIndex, PluginType_Type } from '@peertube/peertube-models' import { PeerTubePlugin, PeerTubePluginIndex, PluginType_Type } from '@peertube/peertube-models'
import { PluginApiService } from './plugin-api.service' import { PluginApiService } from './plugin-api.service'
import { GlobalIconComponent } from '../../../shared/shared-icons/global-icon.component' import { GlobalIconComponent } from '../../../shared/shared-icons/global-icon.component'
@ -9,18 +9,14 @@ import { GlobalIconComponent } from '../../../shared/shared-icons/global-icon.co
styleUrls: [ './plugin-card.component.scss' ], styleUrls: [ './plugin-card.component.scss' ],
imports: [ GlobalIconComponent ] imports: [ GlobalIconComponent ]
}) })
export class PluginCardComponent { export class PluginCardComponent {
@Input() plugin: PeerTubePluginIndex | PeerTubePlugin private pluginApiService = inject(PluginApiService)
@Input() version: string
@Input() pluginType: PluginType_Type
constructor ( readonly plugin = input<PeerTubePluginIndex | PeerTubePlugin>(undefined)
private pluginApiService: PluginApiService readonly version = input<string>(undefined)
) { readonly pluginType = input<PluginType_Type>(undefined)
}
getPluginOrThemeHref (name: string) { getPluginOrThemeHref (name: string) {
return this.pluginApiService.getPluginOrThemeHref(this.pluginType, name) return this.pluginApiService.getPluginOrThemeHref(this.pluginType(), name)
} }
} }

View File

@ -1,13 +1,13 @@
<ng-container> <ng-container>
<a [href]="'mailto:' + entry.email" [title]="getTitle()"> <a [href]="'mailto:' + entry().email" [title]="getTitle()">
@if (showEmailVerifyInformation) { @if (showEmailVerifyInformation()) {
@if (entry.emailVerified === true) { @if (entry().emailVerified === true) {
&#x2713; {{ entry.email }} &#x2713; {{ entry().email }}
} @else { } @else {
<em *ngIf="!entry.emailVerified">? {{ entry.email }}</em> <em *ngIf="!entry().emailVerified">? {{ entry().email }}</em>
} }
} @else { } @else {
{{ entry.email }} {{ entry().email }}
} }
</a> </a>
</ng-container> </ng-container>

View File

@ -1,4 +1,4 @@
import { Component, Input, booleanAttribute } from '@angular/core' import { Component, booleanAttribute, input } from '@angular/core'
import { User, UserRegistration } from '@peertube/peertube-models' import { User, UserRegistration } from '@peertube/peertube-models'
import { NgIf } from '@angular/common' import { NgIf } from '@angular/common'
@ -9,11 +9,11 @@ import { NgIf } from '@angular/common'
imports: [ NgIf ] imports: [ NgIf ]
}) })
export class UserEmailInfoComponent { export class UserEmailInfoComponent {
@Input() entry: User | UserRegistration readonly entry = input<User | UserRegistration>(undefined)
@Input({ transform: booleanAttribute }) showEmailVerifyInformation: boolean readonly showEmailVerifyInformation = input<boolean, unknown>(undefined, { transform: booleanAttribute })
getTitle () { getTitle () {
if (this.entry.emailVerified) { if (this.entry().emailVerified) {
return $localize`User email has been verified` return $localize`User email has been verified`
} }

View File

@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core' import { Component, OnInit, inject, input } from '@angular/core'
import { ServerService } from '@app/core' import { ServerService } from '@app/core'
import { HTMLServerConfig, VideoResolution } from '@peertube/peertube-models' import { HTMLServerConfig, VideoResolution } from '@peertube/peertube-models'
import { BytesPipe } from '../../shared/shared-main/common/bytes.pipe' import { BytesPipe } from '../../shared/shared-main/common/bytes.pipe'
@ -10,12 +10,12 @@ import { NgIf } from '@angular/common'
imports: [ NgIf, BytesPipe ] imports: [ NgIf, BytesPipe ]
}) })
export class UserRealQuotaInfoComponent implements OnInit { export class UserRealQuotaInfoComponent implements OnInit {
@Input() videoQuota: number | string private server = inject(ServerService)
readonly videoQuota = input<number | string>(undefined)
private serverConfig: HTMLServerConfig private serverConfig: HTMLServerConfig
constructor (private server: ServerService) { }
ngOnInit () { ngOnInit () {
this.serverConfig = this.server.getHTMLConfig() this.serverConfig = this.server.getHTMLConfig()
} }
@ -41,6 +41,6 @@ export class UserRealQuotaInfoComponent implements OnInit {
} }
getQuotaAsNumber () { getQuotaAsNumber () {
return parseInt(this.videoQuota + '', 10) return parseInt(this.videoQuota() + '', 10)
} }
} }

View File

@ -1,24 +1,18 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { Notifier } from '@app/core' import { Notifier } from '@app/core'
import { Debug } from '@peertube/peertube-models' import { Debug } from '@peertube/peertube-models'
import { DebugService } from './debug.service' import { DebugService } from './debug.service'
import { GlobalIconComponent } from '@app/shared/shared-icons/global-icon.component'
@Component({ @Component({
templateUrl: './debug.component.html', templateUrl: './debug.component.html',
styleUrls: [ './debug.component.scss' ], styleUrls: [ './debug.component.scss' ],
imports: [ imports: []
GlobalIconComponent
]
}) })
export class DebugComponent implements OnInit { export class DebugComponent implements OnInit {
debug: Debug private debugService = inject(DebugService)
private notifier = inject(Notifier)
constructor ( debug: Debug
private debugService: DebugService,
private notifier: Notifier
) {
}
ngOnInit (): void { ngOnInit (): void {
this.load() this.load()
@ -26,10 +20,10 @@ export class DebugComponent implements OnInit {
load () { load () {
this.debugService.getDebug() this.debugService.getDebug()
.subscribe({ .subscribe({
next: debug => this.debug = debug, next: debug => this.debug = debug,
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
} }

View File

@ -1,24 +1,22 @@
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
import { catchError } from 'rxjs/operators' import { catchError } from 'rxjs/operators'
import { HttpClient } from '@angular/common/http' import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable, inject } from '@angular/core'
import { RestExtractor } from '@app/core' import { RestExtractor } from '@app/core'
import { Debug } from '@peertube/peertube-models' import { Debug } from '@peertube/peertube-models'
import { environment } from '../../../../environments/environment' import { environment } from '../../../../environments/environment'
@Injectable() @Injectable()
export class DebugService { export class DebugService {
private static BASE_DEBUG_URL = environment.apiUrl + '/api/v1/server/debug' private authHttp = inject(HttpClient)
private restExtractor = inject(RestExtractor)
constructor ( private static BASE_DEBUG_URL = environment.apiUrl + '/api/v1/server/debug'
private authHttp: HttpClient,
private restExtractor: RestExtractor
) {}
getDebug (): Observable<Debug> { getDebug (): Observable<Debug> {
return this.authHttp.get<Debug>(DebugService.BASE_DEBUG_URL) return this.authHttp.get<Debug>(DebugService.BASE_DEBUG_URL)
.pipe( .pipe(
catchError(err => this.restExtractor.handleError(err)) catchError(err => this.restExtractor.handleError(err))
) )
} }
} }

View File

@ -2,7 +2,7 @@ import { SortMeta } from 'primeng/api'
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
import { catchError, map } from 'rxjs/operators' import { catchError, map } from 'rxjs/operators'
import { HttpClient, HttpParams } from '@angular/common/http' import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable, inject } from '@angular/core'
import { RestExtractor, RestPagination, RestService } from '@app/core' import { RestExtractor, RestPagination, RestService } from '@app/core'
import { Job, ResultList } from '@peertube/peertube-models' import { Job, ResultList } from '@peertube/peertube-models'
import { environment } from '../../../../environments/environment' import { environment } from '../../../../environments/environment'
@ -11,13 +11,11 @@ import { JobTypeClient } from '../../../../types/job-type-client.type'
@Injectable() @Injectable()
export class JobService { export class JobService {
private static BASE_JOB_URL = environment.apiUrl + '/api/v1/jobs' private authHttp = inject(HttpClient)
private restService = inject(RestService)
private restExtractor = inject(RestExtractor)
constructor ( private static BASE_JOB_URL = environment.apiUrl + '/api/v1/jobs'
private authHttp: HttpClient,
private restService: RestService,
private restExtractor: RestExtractor
) {}
listJobs (options: { listJobs (options: {
jobState?: JobStateClient jobState?: JobStateClient
@ -33,12 +31,12 @@ export class JobService {
if (jobType !== 'all') params = params.append('jobType', jobType) if (jobType !== 'all') params = params.append('jobType', jobType)
return this.authHttp.get<ResultList<Job>>(JobService.BASE_JOB_URL + `/${jobState || ''}`, { params }) return this.authHttp.get<ResultList<Job>>(JobService.BASE_JOB_URL + `/${jobState || ''}`, { params })
.pipe( .pipe(
map(res => this.restExtractor.convertResultListDateToHuman(res, [ 'createdAt', 'processedOn', 'finishedOn' ], 'precise')), map(res => this.restExtractor.convertResultListDateToHuman(res, [ 'createdAt', 'processedOn', 'finishedOn' ], 'precise')),
map(res => this.restExtractor.applyToResultListData(res, this.prettyPrintData.bind(this))), map(res => this.restExtractor.applyToResultListData(res, this.prettyPrintData.bind(this))),
map(res => this.restExtractor.applyToResultListData(res, this.buildUniqId.bind(this))), map(res => this.restExtractor.applyToResultListData(res, this.buildUniqId.bind(this))),
catchError(err => this.restExtractor.handleError(err)) catchError(err => this.restExtractor.handleError(err))
) )
} }
private prettyPrintData (obj: Job) { private prettyPrintData (obj: Job) {

View File

@ -1,9 +1,9 @@
import { NgClass, NgFor, NgIf } from '@angular/common' import { NgClass, NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'
import { Notifier, RestPagination, RestTable } from '@app/core' import { Notifier, RestPagination, RestTable } from '@app/core'
import { SelectOptionsComponent } from '@app/shared/shared-forms/select/select-options.component' import { SelectOptionsComponent } from '@app/shared/shared-forms/select/select-options.component'
import { GlobalIconComponent } from '@app/shared/shared-icons/global-icon.component'
import { AutoColspanDirective } from '@app/shared/shared-main/common/auto-colspan.directive' import { AutoColspanDirective } from '@app/shared/shared-main/common/auto-colspan.directive'
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap' import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'
import { Job, JobState, JobType } from '@peertube/peertube-models' import { Job, JobState, JobType } from '@peertube/peertube-models'
@ -23,7 +23,6 @@ import { JobService } from './job.service'
styleUrls: [ './jobs.component.scss' ], styleUrls: [ './jobs.component.scss' ],
imports: [ imports: [
FormsModule, FormsModule,
NgFor,
NgClass, NgClass,
ButtonComponent, ButtonComponent,
TableModule, TableModule,
@ -31,12 +30,14 @@ import { JobService } from './job.service'
NgIf, NgIf,
NgbTooltip, NgbTooltip,
TableExpanderIconComponent, TableExpanderIconComponent,
GlobalIconComponent,
SelectOptionsComponent, SelectOptionsComponent,
AutoColspanDirective AutoColspanDirective
] ]
}) })
export class JobsComponent extends RestTable implements OnInit { export class JobsComponent extends RestTable implements OnInit {
private notifier = inject(Notifier)
private jobsService = inject(JobService)
private static LS_STATE = 'jobs-list-state' private static LS_STATE = 'jobs-list-state'
private static LS_TYPE = 'jobs-list-type' private static LS_TYPE = 'jobs-list-type'
@ -87,13 +88,6 @@ export class JobsComponent extends RestTable implements OnInit {
sort: SortMeta = { field: 'createdAt', order: -1 } sort: SortMeta = { field: 'createdAt', order: -1 }
pagination: RestPagination = { count: this.rowsPerPage, start: 0 } pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
constructor (
private notifier: Notifier,
private jobsService: JobService
) {
super()
}
ngOnInit () { ngOnInit () {
this.loadJobStateAndType() this.loadJobStateAndType()
this.initialize() this.initialize()

View File

@ -1,5 +1,5 @@
import { NgClass, NgFor, NgIf } from '@angular/common' import { NgClass, NgFor, NgIf } from '@angular/common'
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core' import { Component, ElementRef, OnInit, inject, viewChild } from '@angular/core'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'
import { LocalStorageService, Notifier } from '@app/core' import { LocalStorageService, Notifier } from '@app/core'
import { SelectOptionsComponent } from '@app/shared/shared-forms/select/select-options.component' import { SelectOptionsComponent } from '@app/shared/shared-forms/select/select-options.component'
@ -29,10 +29,14 @@ import { LogsService } from './logs.service'
] ]
}) })
export class LogsComponent implements OnInit { export class LogsComponent implements OnInit {
private logsService = inject(LogsService)
private notifier = inject(Notifier)
private localStorage = inject(LocalStorageService)
private static LS_LOG_TYPE_CHOICE_KEY = 'admin-logs-log-type-choice' private static LS_LOG_TYPE_CHOICE_KEY = 'admin-logs-log-type-choice'
@ViewChild('logsElement', { static: true }) logsElement: ElementRef<HTMLElement> readonly logsElement = viewChild<ElementRef<HTMLElement>>('logsElement')
@ViewChild('logsContent', { static: true }) logsContent: ElementRef<HTMLElement> readonly logsContent = viewChild<ElementRef<HTMLElement>>('logsContent')
loading = false loading = false
@ -48,12 +52,6 @@ export class LogsComponent implements OnInit {
logType: 'audit' | 'standard' logType: 'audit' | 'standard'
tagsOneOf: string[] = [] tagsOneOf: string[] = []
constructor (
private logsService: LogsService,
private notifier: Notifier,
private localStorage: LocalStorageService
) { }
ngOnInit (): void { ngOnInit (): void {
this.buildTimeChoices() this.buildTimeChoices()
this.buildLevelChoices() this.buildLevelChoices()
@ -91,7 +89,7 @@ export class LogsComponent implements OnInit {
this.rawLogs = this.logs.map(l => `${l.level} ${l.localeDate} ${l.message} ${l.meta}`).join('\n') this.rawLogs = this.logs.map(l => `${l.level} ${l.localeDate} ${l.message} ${l.meta}`).join('\n')
setTimeout(() => { setTimeout(() => {
this.logsElement.nativeElement.scrollIntoView({ block: 'end', inline: 'nearest' }) this.logsElement().nativeElement.scrollIntoView({ block: 'end', inline: 'nearest' })
}) })
}, },

View File

@ -1,5 +1,5 @@
import { HttpClient, HttpParams } from '@angular/common/http' import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable, inject } from '@angular/core'
import { RestExtractor, RestService } from '@app/core' import { RestExtractor, RestService } from '@app/core'
import { ServerLogLevel } from '@peertube/peertube-models' import { ServerLogLevel } from '@peertube/peertube-models'
import { catchError, map } from 'rxjs/operators' import { catchError, map } from 'rxjs/operators'
@ -8,15 +8,13 @@ import { LogRow } from './log-row.model'
@Injectable() @Injectable()
export class LogsService { export class LogsService {
private authHttp = inject(HttpClient)
private restService = inject(RestService)
private restExtractor = inject(RestExtractor)
private static BASE_LOG_URL = environment.apiUrl + '/api/v1/server/logs' private static BASE_LOG_URL = environment.apiUrl + '/api/v1/server/logs'
private static BASE_AUDIT_LOG_URL = environment.apiUrl + '/api/v1/server/audit-logs' private static BASE_AUDIT_LOG_URL = environment.apiUrl + '/api/v1/server/audit-logs'
constructor (
private authHttp: HttpClient,
private restService: RestService,
private restExtractor: RestExtractor
) {}
getLogs (options: { getLogs (options: {
isAuditLog: boolean isAuditLog: boolean
startDate: string startDate: string
@ -38,9 +36,9 @@ export class LogsService {
: LogsService.BASE_LOG_URL : LogsService.BASE_LOG_URL
return this.authHttp.get<LogRow[]>(path, { params }) return this.authHttp.get<LogRow[]>(path, { params })
.pipe( .pipe(
map(rows => rows.map(r => new LogRow(r))), map(rows => rows.map(r => new LogRow(r))),
catchError(err => this.restExtractor.handleError(err)) catchError(err => this.restExtractor.handleError(err))
) )
} }
} }

View File

@ -1,6 +1,6 @@
import { NgClass, NgIf } from '@angular/common' import { NgClass, NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { RouterLink } from '@angular/router'
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core' import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
import { formatICU } from '@app/helpers' import { formatICU } from '@app/helpers'
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap' import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'
@ -8,7 +8,7 @@ import { RunnerJob, RunnerJobState } from '@peertube/peertube-models'
import { SharedModule, SortMeta } from 'primeng/api' import { SharedModule, SortMeta } from 'primeng/api'
import { TableModule } from 'primeng/table' import { TableModule } from 'primeng/table'
import { AdvancedInputFilter, AdvancedInputFilterComponent } from '../../../../shared/shared-forms/advanced-input-filter.component' import { AdvancedInputFilter, AdvancedInputFilterComponent } from '../../../../shared/shared-forms/advanced-input-filter.component'
import { GlobalIconComponent } from '../../../../shared/shared-icons/global-icon.component'
import { AutoColspanDirective } from '../../../../shared/shared-main/common/auto-colspan.directive' import { AutoColspanDirective } from '../../../../shared/shared-main/common/auto-colspan.directive'
import { ActionDropdownComponent, DropdownAction } from '../../../../shared/shared-main/buttons/action-dropdown.component' import { ActionDropdownComponent, DropdownAction } from '../../../../shared/shared-main/buttons/action-dropdown.component'
import { ButtonComponent } from '../../../../shared/shared-main/buttons/button.component' import { ButtonComponent } from '../../../../shared/shared-main/buttons/button.component'
@ -19,8 +19,6 @@ import { RunnerJobFormatted, RunnerService } from '../runner.service'
selector: 'my-runner-job-list', selector: 'my-runner-job-list',
templateUrl: './runner-job-list.component.html', templateUrl: './runner-job-list.component.html',
imports: [ imports: [
GlobalIconComponent,
RouterLink,
TableModule, TableModule,
SharedModule, SharedModule,
NgbTooltip, NgbTooltip,
@ -33,7 +31,11 @@ import { RunnerJobFormatted, RunnerService } from '../runner.service'
AutoColspanDirective AutoColspanDirective
] ]
}) })
export class RunnerJobListComponent extends RestTable <RunnerJob> implements OnInit { export class RunnerJobListComponent extends RestTable<RunnerJob> implements OnInit {
private runnerService = inject(RunnerService)
private notifier = inject(Notifier)
private confirmService = inject(ConfirmService)
runnerJobs: RunnerJobFormatted[] = [] runnerJobs: RunnerJobFormatted[] = []
totalRecords = 0 totalRecords = 0
@ -67,14 +69,6 @@ export class RunnerJobListComponent extends RestTable <RunnerJob> implements OnI
} }
] ]
constructor (
private runnerService: RunnerService,
private notifier: Notifier,
private confirmService: ConfirmService
) {
super()
}
ngOnInit () { ngOnInit () {
this.actions = [ this.actions = [
[ [
@ -126,20 +120,20 @@ export class RunnerJobListComponent extends RestTable <RunnerJob> implements OnI
if (res === false) return if (res === false) return
this.runnerService.cancelJobs(jobs) this.runnerService.cancelJobs(jobs)
.subscribe({ .subscribe({
next: () => { next: () => {
this.reloadData() this.reloadData()
this.notifier.success( this.notifier.success(
formatICU( formatICU(
$localize`{count, plural, =1 {Job cancelled} other {{count} jobs cancelled}}`, $localize`{count, plural, =1 {Job cancelled} other {{count} jobs cancelled}}`,
{ count: jobs.length } { count: jobs.length }
)
) )
}, )
},
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
async removeJobs (jobs: RunnerJob[]) { async removeJobs (jobs: RunnerJob[]) {
@ -153,20 +147,20 @@ export class RunnerJobListComponent extends RestTable <RunnerJob> implements OnI
if (res === false) return if (res === false) return
this.runnerService.removeJobs(jobs) this.runnerService.removeJobs(jobs)
.subscribe({ .subscribe({
next: () => { next: () => {
this.reloadData() this.reloadData()
this.notifier.success( this.notifier.success(
formatICU( formatICU(
$localize`{count, plural, =1 {Job removed} other {{count} jobs removed}}`, $localize`{count, plural, =1 {Job removed} other {{count} jobs removed}}`,
{ count: jobs.length } { count: jobs.length }
)
) )
}, )
},
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
getStateBadgeColor (job: RunnerJob) { getStateBadgeColor (job: RunnerJob) {

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core' import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
import { PTDatePipe } from '@app/shared/shared-main/common/date.pipe' import { PTDatePipe } from '@app/shared/shared-main/common/date.pipe'
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap' import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'
@ -21,7 +21,11 @@ import { RunnerService } from '../runner.service'
PTDatePipe PTDatePipe
] ]
}) })
export class RunnerListComponent extends RestTable <Runner> implements OnInit { export class RunnerListComponent extends RestTable<Runner> implements OnInit {
private runnerService = inject(RunnerService)
private notifier = inject(Notifier)
private confirmService = inject(ConfirmService)
runners: Runner[] = [] runners: Runner[] = []
totalRecords = 0 totalRecords = 0
@ -30,14 +34,6 @@ export class RunnerListComponent extends RestTable <Runner> implements OnInit {
actions: DropdownAction<Runner>[][] = [] actions: DropdownAction<Runner>[][] = []
constructor (
private runnerService: RunnerService,
private notifier: Notifier,
private confirmService: ConfirmService
) {
super()
}
ngOnInit () { ngOnInit () {
this.actions = [ this.actions = [
[ [
@ -64,14 +60,14 @@ export class RunnerListComponent extends RestTable <Runner> implements OnInit {
if (res === false) return if (res === false) return
this.runnerService.deleteRunner(runner) this.runnerService.deleteRunner(runner)
.subscribe({ .subscribe({
next: () => { next: () => {
this.reloadData() this.reloadData()
this.notifier.success($localize`Runner removed.`) this.notifier.success($localize`Runner removed.`)
}, },
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
protected reloadDataInternal () { protected reloadDataInternal () {

View File

@ -1,12 +1,12 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { RouterLink } from '@angular/router'
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core' import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
import { PTDatePipe } from '@app/shared/shared-main/common/date.pipe' import { PTDatePipe } from '@app/shared/shared-main/common/date.pipe'
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap' import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'
import { RunnerRegistrationToken } from '@peertube/peertube-models' import { RunnerRegistrationToken } from '@peertube/peertube-models'
import { SharedModule, SortMeta } from 'primeng/api' import { SharedModule, SortMeta } from 'primeng/api'
import { TableModule } from 'primeng/table' import { TableModule } from 'primeng/table'
import { GlobalIconComponent } from '../../../../shared/shared-icons/global-icon.component'
import { ActionDropdownComponent, DropdownAction } from '../../../../shared/shared-main/buttons/action-dropdown.component' import { ActionDropdownComponent, DropdownAction } from '../../../../shared/shared-main/buttons/action-dropdown.component'
import { ButtonComponent } from '../../../../shared/shared-main/buttons/button.component' import { ButtonComponent } from '../../../../shared/shared-main/buttons/button.component'
import { CopyButtonComponent } from '../../../../shared/shared-main/buttons/copy-button.component' import { CopyButtonComponent } from '../../../../shared/shared-main/buttons/copy-button.component'
@ -18,8 +18,6 @@ import { RunnerService } from '../runner.service'
styleUrls: [ './runner-registration-token-list.component.scss' ], styleUrls: [ './runner-registration-token-list.component.scss' ],
templateUrl: './runner-registration-token-list.component.html', templateUrl: './runner-registration-token-list.component.html',
imports: [ imports: [
GlobalIconComponent,
RouterLink,
TableModule, TableModule,
SharedModule, SharedModule,
NgbTooltip, NgbTooltip,
@ -30,7 +28,11 @@ import { RunnerService } from '../runner.service'
PTDatePipe PTDatePipe
] ]
}) })
export class RunnerRegistrationTokenListComponent extends RestTable <RunnerRegistrationToken> implements OnInit { export class RunnerRegistrationTokenListComponent extends RestTable<RunnerRegistrationToken> implements OnInit {
private runnerService = inject(RunnerService)
private notifier = inject(Notifier)
private confirmService = inject(ConfirmService)
registrationTokens: RunnerRegistrationToken[] = [] registrationTokens: RunnerRegistrationToken[] = []
totalRecords = 0 totalRecords = 0
@ -39,14 +41,6 @@ export class RunnerRegistrationTokenListComponent extends RestTable <RunnerRegis
actions: DropdownAction<RunnerRegistrationToken>[][] = [] actions: DropdownAction<RunnerRegistrationToken>[][] = []
constructor (
private runnerService: RunnerService,
private notifier: Notifier,
private confirmService: ConfirmService
) {
super()
}
ngOnInit () { ngOnInit () {
this.actions = [ this.actions = [
[ [
@ -85,14 +79,14 @@ export class RunnerRegistrationTokenListComponent extends RestTable <RunnerRegis
if (res === false) return if (res === false) return
this.runnerService.removeToken(token) this.runnerService.removeToken(token)
.subscribe({ .subscribe({
next: () => { next: () => {
this.reloadData() this.reloadData()
this.notifier.success($localize`Registration token removed.`) this.notifier.success($localize`Registration token removed.`)
}, },
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)
}) })
} }
protected reloadDataInternal () { protected reloadDataInternal () {

View File

@ -1,7 +1,7 @@
import { SortMeta } from 'primeng/api' import { SortMeta } from 'primeng/api'
import { catchError, concatMap, forkJoin, from, map, toArray } from 'rxjs' import { catchError, concatMap, forkJoin, from, map, toArray } from 'rxjs'
import { HttpClient, HttpParams } from '@angular/common/http' import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable, inject } from '@angular/core'
import { RestExtractor, RestPagination, RestService, ServerService } from '@app/core' import { RestExtractor, RestPagination, RestService, ServerService } from '@app/core'
import { arrayify, peertubeTranslate } from '@peertube/peertube-core-utils' import { arrayify, peertubeTranslate } from '@peertube/peertube-core-utils'
import { ResultList, Runner, RunnerJob, RunnerJobAdmin, RunnerJobState, RunnerRegistrationToken } from '@peertube/peertube-models' import { ResultList, Runner, RunnerJob, RunnerJobAdmin, RunnerJobState, RunnerRegistrationToken } from '@peertube/peertube-models'
@ -14,14 +14,12 @@ export type RunnerJobFormatted = RunnerJob & {
@Injectable() @Injectable()
export class RunnerService { export class RunnerService {
private static BASE_RUNNER_URL = environment.apiUrl + '/api/v1/runners' private authHttp = inject(HttpClient)
private server = inject(ServerService)
private restService = inject(RestService)
private restExtractor = inject(RestExtractor)
constructor ( private static BASE_RUNNER_URL = environment.apiUrl + '/api/v1/runners'
private authHttp: HttpClient,
private server: ServerService,
private restService: RestService,
private restExtractor: RestExtractor
) {}
listRegistrationTokens (options: { listRegistrationTokens (options: {
pagination: RestPagination pagination: RestPagination
@ -33,7 +31,7 @@ export class RunnerService {
params = this.restService.addRestGetParams(params, pagination, sort) params = this.restService.addRestGetParams(params, pagination, sort)
return this.authHttp.get<ResultList<RunnerRegistrationToken>>(RunnerService.BASE_RUNNER_URL + '/registration-tokens', { params }) return this.authHttp.get<ResultList<RunnerRegistrationToken>>(RunnerService.BASE_RUNNER_URL + '/registration-tokens', { params })
.pipe(catchError(res => this.restExtractor.handleError(res))) .pipe(catchError(res => this.restExtractor.handleError(res)))
} }
generateToken () { generateToken () {
@ -147,7 +145,7 @@ export class RunnerService {
params = this.restService.addRestGetParams(params, pagination, sort) params = this.restService.addRestGetParams(params, pagination, sort)
return this.authHttp.get<ResultList<Runner>>(RunnerService.BASE_RUNNER_URL + '/', { params }) return this.authHttp.get<ResultList<Runner>>(RunnerService.BASE_RUNNER_URL + '/', { params })
.pipe(catchError(res => this.restExtractor.handleError(res))) .pipe(catchError(res => this.restExtractor.handleError(res)))
} }
deleteRunner (runner: Runner) { deleteRunner (runner: Runner) {

View File

@ -1,5 +1,4 @@
import { NgIf } from '@angular/common' import { Component, OnInit, inject } from '@angular/core'
import { Component, OnInit } from '@angular/core'
import { Title } from '@angular/platform-browser' import { Title } from '@angular/platform-browser'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { LoginLinkComponent } from '@app/shared/shared-main/users/login-link.component' import { LoginLinkComponent } from '@app/shared/shared-main/users/login-link.component'
@ -9,16 +8,16 @@ import { HttpStatusCode, HttpStatusCodeType } from '@peertube/peertube-models'
selector: 'my-error-page', selector: 'my-error-page',
templateUrl: './error-page.component.html', templateUrl: './error-page.component.html',
styleUrls: [ './error-page.component.scss' ], styleUrls: [ './error-page.component.scss' ],
imports: [ NgIf, LoginLinkComponent ] imports: [ LoginLinkComponent ]
}) })
export class ErrorPageComponent implements OnInit { export class ErrorPageComponent implements OnInit {
private titleService = inject(Title)
private router = inject(Router)
status: HttpStatusCodeType = HttpStatusCode.NOT_FOUND_404 status: HttpStatusCodeType = HttpStatusCode.NOT_FOUND_404
type: 'video' | 'other' = 'other' type: 'video' | 'other' = 'other'
public constructor ( public constructor () {
private titleService: Title,
private router: Router
) {
const state = this.router.getCurrentNavigation()?.extras.state const state = this.router.getCurrentNavigation()?.extras.state
this.type = state?.type || this.type this.type = state?.type || this.type
this.status = state?.obj.status || this.status this.status = state?.obj.status || this.status

View File

@ -1,4 +1,4 @@
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core' import { Component, ElementRef, OnInit, inject, viewChild } from '@angular/core'
import { CustomPageService } from '@app/shared/shared-main/custom-page/custom-page.service' import { CustomPageService } from '@app/shared/shared-main/custom-page/custom-page.service'
import { CustomMarkupContainerComponent } from '../shared/shared-custom-markup/custom-markup-container.component' import { CustomMarkupContainerComponent } from '../shared/shared-custom-markup/custom-markup-container.component'
@ -6,16 +6,13 @@ import { CustomMarkupContainerComponent } from '../shared/shared-custom-markup/c
templateUrl: './home.component.html', templateUrl: './home.component.html',
imports: [ CustomMarkupContainerComponent ] imports: [ CustomMarkupContainerComponent ]
}) })
export class HomeComponent implements OnInit { export class HomeComponent implements OnInit {
@ViewChild('contentWrapper') contentWrapper: ElementRef<HTMLInputElement> private customPageService = inject(CustomPageService)
readonly contentWrapper = viewChild<ElementRef<HTMLInputElement>>('contentWrapper')
homepageContent: string homepageContent: string
constructor (
private customPageService: CustomPageService
) { }
ngOnInit () { ngOnInit () {
this.customPageService.getInstanceHomepage() this.customPageService.getInstanceHomepage()
.subscribe(({ content }) => this.homepageContent = content) .subscribe(({ content }) => this.homepageContent = content)

View File

@ -1,5 +1,5 @@
import { NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common' import { NgClass, NgFor, NgIf } from '@angular/common'
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core' import { AfterViewInit, Component, ElementRef, OnInit, inject, viewChild } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { ActivatedRoute, Router, RouterLink } from '@angular/router' import { ActivatedRoute, Router, RouterLink } from '@angular/router'
import { AuthService, Notifier, RedirectService, SessionStorageService, UserService } from '@app/core' import { AuthService, Notifier, RedirectService, SessionStorageService, UserService } from '@app/core'
@ -32,7 +32,6 @@ import { PluginSelectorDirective } from '../shared/shared-main/plugins/plugin-se
ReactiveFormsModule, ReactiveFormsModule,
AutofocusDirective, AutofocusDirective,
NgClass, NgClass,
NgTemplateOutlet,
InputTextComponent, InputTextComponent,
NgFor, NgFor,
InstanceBannerComponent, InstanceBannerComponent,
@ -41,13 +40,23 @@ import { PluginSelectorDirective } from '../shared/shared-main/plugins/plugin-se
AlertComponent AlertComponent
] ]
}) })
export class LoginComponent extends FormReactive implements OnInit, AfterViewInit { export class LoginComponent extends FormReactive implements OnInit, AfterViewInit {
protected formReactiveService = inject(FormReactiveService)
private route = inject(ActivatedRoute)
private modalService = inject(NgbModal)
private authService = inject(AuthService)
private userService = inject(UserService)
private redirectService = inject(RedirectService)
private notifier = inject(Notifier)
private hooks = inject(HooksService)
private storage = inject(SessionStorageService)
private router = inject(Router)
private static SESSION_STORAGE_REDIRECT_URL_KEY = 'login-previous-url' private static SESSION_STORAGE_REDIRECT_URL_KEY = 'login-previous-url'
@ViewChild('forgotPasswordModal', { static: true }) forgotPasswordModal: ElementRef readonly forgotPasswordModal = viewChild<ElementRef>('forgotPasswordModal')
@ViewChild('otpTokenInput') otpTokenInput: InputTextComponent readonly otpTokenInput = viewChild<InputTextComponent>('otpTokenInput')
@ViewChild('instanceAboutAccordion') instanceAboutAccordion: InstanceAboutAccordionComponent readonly instanceAboutAccordion = viewChild<InstanceAboutAccordionComponent>('instanceAboutAccordion')
accordion: NgbAccordionDirective accordion: NgbAccordionDirective
error: string = null error: string = null
@ -72,21 +81,6 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
private openedForgotPasswordModal: NgbModalRef private openedForgotPasswordModal: NgbModalRef
private serverConfig: ServerConfig private serverConfig: ServerConfig
constructor (
protected formReactiveService: FormReactiveService,
private route: ActivatedRoute,
private modalService: NgbModal,
private authService: AuthService,
private userService: UserService,
private redirectService: RedirectService,
private notifier: Notifier,
private hooks: HooksService,
private storage: SessionStorageService,
private router: Router
) {
super()
}
get signupAllowed () { get signupAllowed () {
return this.serverConfig.signup.allowed === true return this.serverConfig.signup.allowed === true
} }
@ -98,8 +92,9 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
onTermsClick (event: Event, instanceInformation: HTMLElement) { onTermsClick (event: Event, instanceInformation: HTMLElement) {
event.preventDefault() event.preventDefault()
if (this.instanceAboutAccordion) { const instanceAboutAccordion = this.instanceAboutAccordion()
this.instanceAboutAccordion.expandTerms() if (instanceAboutAccordion) {
instanceAboutAccordion.expandTerms()
instanceInformation.scrollIntoView({ behavior: 'smooth' }) instanceInformation.scrollIntoView({ behavior: 'smooth' })
} }
} }
@ -191,7 +186,7 @@ The link will expire within 1 hour.`
} }
openForgotPasswordModal () { openForgotPasswordModal () {
this.openedForgotPasswordModal = this.modalService.open(this.forgotPasswordModal) this.openedForgotPasswordModal = this.modalService.open(this.forgotPasswordModal())
} }
hideForgotPasswordModal () { hideForgotPasswordModal () {
@ -199,7 +194,7 @@ The link will expire within 1 hour.`
} }
onInstanceAboutAccordionInit (instanceAboutAccordion: InstanceAboutAccordionComponent) { onInstanceAboutAccordionInit (instanceAboutAccordion: InstanceAboutAccordionComponent) {
this.accordion = instanceAboutAccordion.accordion this.accordion = instanceAboutAccordion.accordion()
} }
private loadExternalAuthToken (username: string, token: string) { private loadExternalAuthToken (username: string, token: string) {
@ -230,7 +225,7 @@ The link will expire within 1 hour.`
setTimeout(() => { setTimeout(() => {
this.form.get('otp-token').setValidators(USER_OTP_TOKEN_VALIDATOR.VALIDATORS) this.form.get('otp-token').setValidators(USER_OTP_TOKEN_VALIDATOR.VALIDATORS)
this.otpTokenInput.focus() this.otpTokenInput().focus()
}) })
return return

View File

@ -1,13 +1,11 @@
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { AbuseListTableComponent } from '../../shared/shared-abuse-list/abuse-list-table.component' import { AbuseListTableComponent } from '../../shared/shared-abuse-list/abuse-list-table.component'
import { GlobalIconComponent } from '../../shared/shared-icons/global-icon.component'
@Component({ @Component({
selector: 'my-account-abuses-list', selector: 'my-account-abuses-list',
templateUrl: './my-account-abuses-list.component.html', templateUrl: './my-account-abuses-list.component.html',
styleUrls: [], styleUrls: [],
imports: [ GlobalIconComponent, AbuseListTableComponent ] imports: [ AbuseListTableComponent ]
}) })
export class MyAccountAbusesListComponent { export class MyAccountAbusesListComponent {
} }

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { AuthService, ConfirmService, Notifier, ScopedTokensService } from '@app/core' import { AuthService, ConfirmService, Notifier, ScopedTokensService } from '@app/core'
import { VideoService } from '@app/shared/shared-main/video/video.service' import { VideoService } from '@app/shared/shared-main/video/video.service'
import { FeedFormat, ScopedToken } from '@peertube/peertube-models' import { FeedFormat, ScopedToken } from '@peertube/peertube-models'
@ -12,19 +12,17 @@ import { InputTextComponent } from '../../shared/shared-forms/input-text.compone
imports: [ InputTextComponent ] imports: [ InputTextComponent ]
}) })
export class MyAccountApplicationsComponent implements OnInit { export class MyAccountApplicationsComponent implements OnInit {
private authService = inject(AuthService)
private scopedTokensService = inject(ScopedTokensService)
private videoService = inject(VideoService)
private notifier = inject(Notifier)
private confirmService = inject(ConfirmService)
feedUrl: string feedUrl: string
feedToken: string feedToken: string
private baseURL = environment.originServerUrl || window.location.origin private baseURL = environment.originServerUrl || window.location.origin
constructor (
private authService: AuthService,
private scopedTokensService: ScopedTokensService,
private videoService: VideoService,
private notifier: Notifier,
private confirmService: ConfirmService
) {}
ngOnInit () { ngOnInit () {
this.feedUrl = this.baseURL this.feedUrl = this.baseURL

View File

@ -1,5 +1,5 @@
import { NgFor, NgIf } from '@angular/common' import { NgFor, NgIf } from '@angular/common'
import { Component, Input, OnInit, ViewChild } from '@angular/core' import { Component, OnInit, inject, input, viewChild } from '@angular/core'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'
import { AuthService, ServerService } from '@app/core' import { AuthService, ServerService } from '@app/core'
import { AlertComponent } from '@app/shared/shared-main/common/alert.component' import { AlertComponent } from '@app/shared/shared-main/common/alert.component'
@ -19,9 +19,14 @@ import { UserImportExportService } from './user-import-export.service'
imports: [ NgIf, NgFor, GlobalIconComponent, PeertubeCheckboxComponent, FormsModule, PTDatePipe, BytesPipe, AlertComponent ] imports: [ NgIf, NgFor, GlobalIconComponent, PeertubeCheckboxComponent, FormsModule, PTDatePipe, BytesPipe, AlertComponent ]
}) })
export class MyAccountExportComponent implements OnInit { export class MyAccountExportComponent implements OnInit {
@ViewChild('exportModal', { static: true }) exportModal: NgbModal private authService = inject(AuthService)
private server = inject(ServerService)
private userImportExportService = inject(UserImportExportService)
private modalService = inject(NgbModal)
@Input() videoQuotaUsed: number readonly exportModal = viewChild<NgbModal>('exportModal')
readonly videoQuotaUsed = input<number>(undefined)
userExports: UserExport[] = [] userExports: UserExport[] = []
@ -33,15 +38,8 @@ export class MyAccountExportComponent implements OnInit {
private exportModalOpened: NgbModalRef private exportModalOpened: NgbModalRef
private requestingArchive = false private requestingArchive = false
constructor (
private authService: AuthService,
private server: ServerService,
private userImportExportService: UserImportExportService,
private modalService: NgbModal
) {}
ngOnInit () { ngOnInit () {
this.archiveWeightEstimation = this.videoQuotaUsed this.archiveWeightEstimation = this.videoQuotaUsed()
this.reloadUserExports() this.reloadUserExports()
} }
@ -70,7 +68,7 @@ export class MyAccountExportComponent implements OnInit {
this.exportWithVideosFiles = false this.exportWithVideosFiles = false
this.errorInModal = undefined this.errorInModal = undefined
this.exportModalOpened = this.modalService.open(this.exportModal, { centered: true }) this.exportModalOpened = this.modalService.open(this.exportModal(), { centered: true })
} }
requestNewArchive () { requestNewArchive () {
@ -105,8 +103,9 @@ export class MyAccountExportComponent implements OnInit {
const error = err.body as PeerTubeProblemDocument const error = err.body as PeerTubeProblemDocument
if (error.code === ServerErrorCode.MAX_USER_VIDEO_QUOTA_EXCEEDED_FOR_USER_EXPORT) { if (error.code === ServerErrorCode.MAX_USER_VIDEO_QUOTA_EXCEEDED_FOR_USER_EXPORT) {
// eslint-disable-next-line max-len this.errorInModal =
this.errorInModal = $localize`Video files cannot be included in the export because you have exceeded the maximum video quota allowed by your administrator to export this archive.` // eslint-disable-next-line max-len
$localize`Video files cannot be included in the export because you have exceeded the maximum video quota allowed by your administrator to export this archive.`
return return
} }

View File

@ -1,29 +1,27 @@
import { Component, OnInit, ViewChild } from '@angular/core' import { Component, OnInit, inject, viewChild } from '@angular/core'
import { CanComponentDeactivate, UserService } from '@app/core' import { CanComponentDeactivate, UserService } from '@app/core'
import { GlobalIconComponent } from '../../shared/shared-icons/global-icon.component'
import { MyAccountExportComponent } from './my-account-export.component' import { MyAccountExportComponent } from './my-account-export.component'
import { MyAccountImportComponent } from './my-account-import.component' import { MyAccountImportComponent } from './my-account-import.component'
@Component({ @Component({
selector: 'my-account-import-export', selector: 'my-account-import-export',
templateUrl: './my-account-import-export.component.html', templateUrl: './my-account-import-export.component.html',
imports: [ GlobalIconComponent, MyAccountImportComponent, MyAccountExportComponent ] imports: [ MyAccountImportComponent, MyAccountExportComponent ]
}) })
export class MyAccountImportExportComponent implements OnInit, CanComponentDeactivate { export class MyAccountImportExportComponent implements OnInit, CanComponentDeactivate {
@ViewChild('accountImport') accountImport: MyAccountImportComponent private userService = inject(UserService)
readonly accountImport = viewChild<MyAccountImportComponent>('accountImport')
videoQuotaUsed: number videoQuotaUsed: number
constructor (
private userService: UserService
) {}
ngOnInit () { ngOnInit () {
this.userService.getMyVideoQuotaUsed() this.userService.getMyVideoQuotaUsed()
.subscribe(res => this.videoQuotaUsed = res.videoQuotaUsed) .subscribe(res => this.videoQuotaUsed = res.videoQuotaUsed)
} }
canDeactivate () { canDeactivate () {
return this.accountImport?.canDeactivate() || { canDeactivate: true } return this.accountImport()?.canDeactivate() || { canDeactivate: true }
} }
} }

View File

@ -1,6 +1,6 @@
import { NgIf } from '@angular/common' import { NgIf } from '@angular/common'
import { HttpErrorResponse } from '@angular/common/http' import { HttpErrorResponse } from '@angular/common/http'
import { Component, Input, OnDestroy, OnInit } from '@angular/core' import { Component, OnDestroy, OnInit, inject, input } from '@angular/core'
import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core' import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core'
import { buildHTTPErrorResponse, genericUploadErrorHandler, getUploadXRetryConfig } from '@app/helpers' import { buildHTTPErrorResponse, genericUploadErrorHandler, getUploadXRetryConfig } from '@app/helpers'
import { AlertComponent } from '@app/shared/shared-main/common/alert.component' import { AlertComponent } from '@app/shared/shared-main/common/alert.component'
@ -20,7 +20,13 @@ import { UserImportExportService } from './user-import-export.service'
imports: [ NgIf, UploadProgressComponent, NgbTooltip, PTDatePipe, AlertComponent ] imports: [ NgIf, UploadProgressComponent, NgbTooltip, PTDatePipe, AlertComponent ]
}) })
export class MyAccountImportComponent implements OnInit, OnDestroy, CanComponentDeactivate { export class MyAccountImportComponent implements OnInit, OnDestroy, CanComponentDeactivate {
@Input() videoQuotaUsed: number private authService = inject(AuthService)
private server = inject(ServerService)
private userImportExportService = inject(UserImportExportService)
private resumableUploadService = inject(UploadxService)
private notifier = inject(Notifier)
readonly videoQuotaUsed = input<number>(undefined)
uploadingArchive = false uploadingArchive = false
archiveUploadFinished = false archiveUploadFinished = false
@ -35,14 +41,6 @@ export class MyAccountImportComponent implements OnInit, OnDestroy, CanComponent
private uploadServiceSubscription: Subscription private uploadServiceSubscription: Subscription
private alreadyRefreshedToken = false private alreadyRefreshedToken = false
constructor (
private authService: AuthService,
private server: ServerService,
private userImportExportService: UserImportExportService,
private resumableUploadService: UploadxService,
private notifier: Notifier
) {}
ngOnInit () { ngOnInit () {
this.userImportExportService.getLatestImport({ userId: this.authService.getUser().id }) this.userImportExportService.getLatestImport({ userId: this.authService.getUser().id })
.subscribe(res => this.latestImport = res) .subscribe(res => this.latestImport = res)
@ -113,10 +111,10 @@ export class MyAccountImportComponent implements OnInit, OnDestroy, CanComponent
const user = this.authService.getUser() const user = this.authService.getUser()
if (user.videoQuota !== -1 && this.videoQuotaUsed + file.size > user.videoQuota) { if (user.videoQuota !== -1 && this.videoQuotaUsed() + file.size > user.videoQuota) {
const bytePipes = new BytesPipe() const bytePipes = new BytesPipe()
const fileSizeBytes = bytePipes.transform(file.size, 0) const fileSizeBytes = bytePipes.transform(file.size, 0)
const videoQuotaUsedBytes = bytePipes.transform(this.videoQuotaUsed, 0) const videoQuotaUsedBytes = bytePipes.transform(this.videoQuotaUsed(), 0)
const videoQuotaBytes = bytePipes.transform(user.videoQuota, 0) const videoQuotaBytes = bytePipes.transform(user.videoQuota, 0)
this.notifier.error( this.notifier.error(

View File

@ -1,6 +1,6 @@
import { catchError, map } from 'rxjs/operators' import { catchError, map } from 'rxjs/operators'
import { HttpClient } from '@angular/common/http' import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable, inject } from '@angular/core'
import { RestExtractor, ServerService } from '@app/core' import { RestExtractor, ServerService } from '@app/core'
import { environment } from 'src/environments/environment' import { environment } from 'src/environments/environment'
import { HttpStatusCode, ResultList, UserExport, UserImport } from '@peertube/peertube-models' import { HttpStatusCode, ResultList, UserExport, UserImport } from '@peertube/peertube-models'
@ -9,15 +9,13 @@ import { peertubeTranslate } from '@peertube/peertube-core-utils'
@Injectable() @Injectable()
export class UserImportExportService { export class UserImportExportService {
private authHttp = inject(HttpClient)
private restExtractor = inject(RestExtractor)
private server = inject(ServerService)
static BASE_USER_EXPORTS_URL = environment.apiUrl + '/api/v1/users/' static BASE_USER_EXPORTS_URL = environment.apiUrl + '/api/v1/users/'
static BASE_USER_IMPORTS_URL = environment.apiUrl + '/api/v1/users/' static BASE_USER_IMPORTS_URL = environment.apiUrl + '/api/v1/users/'
constructor (
private authHttp: HttpClient,
private restExtractor: RestExtractor,
private server: ServerService
) { }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
listUserExports (options: { listUserExports (options: {

View File

@ -1,4 +1,4 @@
import { Component, ViewChild } from '@angular/core' import { Component, viewChild } from '@angular/core'
import { UserNotificationsComponent } from '@app/shared/standalone-notifications/user-notifications.component' import { UserNotificationsComponent } from '@app/shared/standalone-notifications/user-notifications.component'
import { NgIf } from '@angular/common' import { NgIf } from '@angular/common'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'
@ -13,7 +13,7 @@ type NotificationSortType = 'createdAt' | 'read'
imports: [ RouterLink, GlobalIconComponent, FormsModule, NgIf, UserNotificationsComponent ] imports: [ RouterLink, GlobalIconComponent, FormsModule, NgIf, UserNotificationsComponent ]
}) })
export class MyAccountNotificationsComponent { export class MyAccountNotificationsComponent {
@ViewChild('userNotification', { static: true }) userNotification: UserNotificationsComponent readonly userNotification = viewChild<UserNotificationsComponent>('userNotification')
_notificationSortType: NotificationSortType = 'createdAt' _notificationSortType: NotificationSortType = 'createdAt'
@ -28,14 +28,14 @@ export class MyAccountNotificationsComponent {
} }
markAllAsRead () { markAllAsRead () {
this.userNotification.markAllAsRead() this.userNotification().markAllAsRead()
} }
hasUnreadNotifications () { hasUnreadNotifications () {
return this.userNotification.notifications.filter(n => n.read === false).length !== 0 return this.userNotification().notifications.filter(n => n.read === false).length !== 0
} }
onChangeSortColumn () { onChangeSortColumn () {
this.userNotification.changeSortColumn(this.notificationSortType) this.userNotification().changeSortColumn(this.notificationSortType)
} }
} }

View File

@ -1,5 +1,5 @@
import { NgClass, NgIf } from '@angular/common' import { NgClass, NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { AuthService, ServerService, UserService } from '@app/core' import { AuthService, ServerService, UserService } from '@app/core'
import { USER_EMAIL_VALIDATOR, USER_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators' import { USER_EMAIL_VALIDATOR, USER_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators'
@ -18,19 +18,15 @@ import { InputTextComponent } from '../../../shared/shared-forms/input-text.comp
imports: [ NgIf, FormsModule, ReactiveFormsModule, NgClass, InputTextComponent, AlertComponent ] imports: [ NgIf, FormsModule, ReactiveFormsModule, NgClass, InputTextComponent, AlertComponent ]
}) })
export class MyAccountChangeEmailComponent extends FormReactive implements OnInit { export class MyAccountChangeEmailComponent extends FormReactive implements OnInit {
protected formReactiveService = inject(FormReactiveService)
private authService = inject(AuthService)
private userService = inject(UserService)
private serverService = inject(ServerService)
error: string error: string
success: string success: string
user: User user: User
constructor (
protected formReactiveService: FormReactiveService,
private authService: AuthService,
private userService: UserService,
private serverService: ServerService
) {
super()
}
ngOnInit () { ngOnInit () {
this.buildForm({ this.buildForm({
'new-email': USER_EMAIL_VALIDATOR, 'new-email': USER_EMAIL_VALIDATOR,

View File

@ -1,5 +1,5 @@
import { NgIf } from '@angular/common' import { NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { AuthService, Notifier, UserService } from '@app/core' import { AuthService, Notifier, UserService } from '@app/core'
import { import {
@ -21,18 +21,14 @@ import { InputTextComponent } from '../../../shared/shared-forms/input-text.comp
imports: [ NgIf, FormsModule, ReactiveFormsModule, InputTextComponent, AlertComponent ] imports: [ NgIf, FormsModule, ReactiveFormsModule, InputTextComponent, AlertComponent ]
}) })
export class MyAccountChangePasswordComponent extends FormReactive implements OnInit { export class MyAccountChangePasswordComponent extends FormReactive implements OnInit {
protected formReactiveService = inject(FormReactiveService)
private notifier = inject(Notifier)
private authService = inject(AuthService)
private userService = inject(UserService)
error: string error: string
user: User user: User
constructor (
protected formReactiveService: FormReactiveService,
private notifier: Notifier,
private authService: AuthService,
private userService: UserService
) {
super()
}
ngOnInit () { ngOnInit () {
this.buildForm({ this.buildForm({
'current-password': USER_EXISTING_PASSWORD_VALIDATOR, 'current-password': USER_EXISTING_PASSWORD_VALIDATOR,
@ -45,8 +41,8 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
const confirmPasswordControl = this.form.get('new-confirmed-password') const confirmPasswordControl = this.form.get('new-confirmed-password')
confirmPasswordControl.valueChanges confirmPasswordControl.valueChanges
.pipe(filter(v => v !== this.form.value['new-password'])) .pipe(filter(v => v !== this.form.value['new-password']))
.subscribe(() => confirmPasswordControl.setErrors({ matchPassword: true })) .subscribe(() => confirmPasswordControl.setErrors({ matchPassword: true }))
} }
changePassword () { changePassword () {

View File

@ -1,4 +1,4 @@
import { Component, Input } from '@angular/core' import { Component, inject, input } from '@angular/core'
import { AuthService, ConfirmService, Notifier, RedirectService, User, UserService } from '@app/core' import { AuthService, ConfirmService, Notifier, RedirectService, User, UserService } from '@app/core'
@Component({ @Component({
@ -7,27 +7,24 @@ import { AuthService, ConfirmService, Notifier, RedirectService, User, UserServi
standalone: true standalone: true
}) })
export class MyAccountDangerZoneComponent { export class MyAccountDangerZoneComponent {
@Input() user: User private authService = inject(AuthService)
private notifier = inject(Notifier)
private userService = inject(UserService)
private confirmService = inject(ConfirmService)
private redirectService = inject(RedirectService)
constructor ( readonly user = input<User>(undefined)
private authService: AuthService,
private notifier: Notifier,
private userService: UserService,
private confirmService: ConfirmService,
private redirectService: RedirectService
) { }
async deleteMe () { async deleteMe () {
const res = await this.confirmService.confirmWithExpectedInput( const res = await this.confirmService.confirmWithExpectedInput(
$localize`Are you sure you want to delete your account?` + $localize`Are you sure you want to delete your account?` +
'<br /><br />' + '<br /><br />' +
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
$localize`This will delete all your data, including channels, videos, comments and you won't be able to create another user on this instance with "${this.user.username}" username.` + $localize`This will delete all your data, including channels, videos, comments and you won't be able to create another user on this instance with "${this.user().username}" username.` +
'<br /><br />' + '<br /><br />' +
$localize`Content cached by other servers and other third-parties might make longer to be deleted.`, $localize`Content cached by other servers and other third-parties might make longer to be deleted.`,
$localize`Type your username to confirm`, $localize`Type your username to confirm`,
this.user.username, this.user().username,
$localize`Delete your account`, $localize`Delete your account`,
$localize`Delete my account` $localize`Delete my account`
) )

View File

@ -7,7 +7,7 @@
> >
<ng-container ngProjectAs="description"> <ng-container ngProjectAs="description">
<p class="mb-0"> <p class="mb-0">
@if (user.emailVerified) { @if (user().emailVerified) {
<ng-container i18n>Necessary to claim podcast RSS feeds.</ng-container> <ng-container i18n>Necessary to claim podcast RSS feeds.</ng-container>
} @else { } @else {
<ng-container i18n>⚠️ Your email cannot be used in podcast RSS feeds because it has not yet been verified.</ng-container> <ng-container i18n>⚠️ Your email cannot be used in podcast RSS feeds because it has not yet been verified.</ng-container>

View File

@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core' import { Component, OnInit, inject, input } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { Notifier, UserService } from '@app/core' import { Notifier, UserService } from '@app/core'
import { FormReactive } from '@app/shared/shared-forms/form-reactive' import { FormReactive } from '@app/shared/shared-forms/form-reactive'
@ -13,22 +13,18 @@ import { PeertubeCheckboxComponent } from '../../../shared/shared-forms/peertube
imports: [ FormsModule, ReactiveFormsModule, PeertubeCheckboxComponent ] imports: [ FormsModule, ReactiveFormsModule, PeertubeCheckboxComponent ]
}) })
export class MyAccountEmailPreferencesComponent extends FormReactive implements OnInit { export class MyAccountEmailPreferencesComponent extends FormReactive implements OnInit {
@Input() user: User protected formReactiveService = inject(FormReactiveService)
private userService = inject(UserService)
private notifier = inject(Notifier)
constructor ( readonly user = input<User>(undefined)
protected formReactiveService: FormReactiveService,
private userService: UserService,
private notifier: Notifier
) {
super()
}
ngOnInit () { ngOnInit () {
this.buildForm({ this.buildForm({
'email-public': null 'email-public': null
}) })
this.form.patchValue({ 'email-public': this.user.emailPublic }) this.form.patchValue({ 'email-public': this.user().emailPublic })
} }
updateEmailPublic () { updateEmailPublic () {
@ -42,7 +38,7 @@ export class MyAccountEmailPreferencesComponent extends FormReactive implements
if (details.emailPublic) this.notifier.success($localize`Email is now public`) if (details.emailPublic) this.notifier.success($localize`Email is now public`)
else this.notifier.success($localize`Email is now private`) else this.notifier.success($localize`Email is now private`)
this.user.emailPublic = details.emailPublic this.user().emailPublic = details.emailPublic
}, },
error: err => this.notifier.error(err.message) error: err => this.notifier.error(err.message)

View File

@ -1,5 +1,5 @@
import { NgFor, NgIf } from '@angular/common' import { NgFor, NgIf } from '@angular/common'
import { Component, Input, OnInit } from '@angular/core' import { Component, OnInit, inject, input } from '@angular/core'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'
import { Notifier, ServerService, User } from '@app/core' import { Notifier, ServerService, User } from '@app/core'
import { UserNotificationService } from '@app/shared/shared-main/users/user-notification.service' import { UserNotificationService } from '@app/shared/shared-main/users/user-notification.service'
@ -15,22 +15,22 @@ import { InputSwitchComponent } from '../../../shared/shared-forms/input-switch.
imports: [ NgIf, NgFor, InputSwitchComponent, FormsModule ] imports: [ NgIf, NgFor, InputSwitchComponent, FormsModule ]
}) })
export class MyAccountNotificationPreferencesComponent implements OnInit { export class MyAccountNotificationPreferencesComponent implements OnInit {
@Input() user: User private userNotificationService = inject(UserNotificationService)
private serverService = inject(ServerService)
private notifier = inject(Notifier)
readonly user = input<User>(undefined)
notificationSettingGroups: { label: string, keys: (keyof UserNotificationSetting)[] }[] = [] notificationSettingGroups: { label: string, keys: (keyof UserNotificationSetting)[] }[] = []
emailNotifications: { [ id in keyof UserNotificationSetting ]?: boolean } = {} emailNotifications: { [id in keyof UserNotificationSetting]?: boolean } = {}
webNotifications: { [ id in keyof UserNotificationSetting ]?: boolean } = {} webNotifications: { [id in keyof UserNotificationSetting]?: boolean } = {}
labelNotifications: { [ id in keyof UserNotificationSetting ]?: string } = {} labelNotifications: { [id in keyof UserNotificationSetting]?: string } = {}
rightNotifications: { [ id in keyof Partial<UserNotificationSetting> ]?: UserRightType } = {} rightNotifications: { [id in keyof Partial<UserNotificationSetting>]?: UserRightType } = {}
emailEnabled = false emailEnabled = false
private savePreferences = debounce(this.savePreferencesImpl.bind(this), 500) private savePreferences = debounce(this.savePreferencesImpl.bind(this), 500)
constructor ( constructor () {
private userNotificationService: UserNotificationService,
private serverService: ServerService,
private notifier: Notifier
) {
this.notificationSettingGroups = [ this.notificationSettingGroups = [
{ {
label: $localize`Social`, label: $localize`Social`,
@ -118,7 +118,7 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
const rightToHave = this.rightNotifications[field] const rightToHave = this.rightNotifications[field]
if (!rightToHave) return true // No rights needed if (!rightToHave) return true // No rights needed
return this.user.hasRight(rightToHave) return this.user().hasRight(rightToHave)
} }
hasNotificationsInGroup (group: { keys: (keyof UserNotificationSetting)[] }) { hasNotificationsInGroup (group: { keys: (keyof UserNotificationSetting)[] }) {
@ -134,21 +134,21 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
} }
updateEmailSetting (field: keyof UserNotificationSetting, value: boolean) { updateEmailSetting (field: keyof UserNotificationSetting, value: boolean) {
if (value === true) this.user.notificationSettings[field] |= UserNotificationSettingValue.EMAIL if (value === true) this.user().notificationSettings[field] |= UserNotificationSettingValue.EMAIL
else this.user.notificationSettings[field] &= ~UserNotificationSettingValue.EMAIL else this.user().notificationSettings[field] &= ~UserNotificationSettingValue.EMAIL
this.savePreferences() this.savePreferences()
} }
updateWebSetting (field: keyof UserNotificationSetting, value: boolean) { updateWebSetting (field: keyof UserNotificationSetting, value: boolean) {
if (value === true) this.user.notificationSettings[field] |= UserNotificationSettingValue.WEB if (value === true) this.user().notificationSettings[field] |= UserNotificationSettingValue.WEB
else this.user.notificationSettings[field] &= ~UserNotificationSettingValue.WEB else this.user().notificationSettings[field] &= ~UserNotificationSettingValue.WEB
this.savePreferences() this.savePreferences()
} }
private savePreferencesImpl () { private savePreferencesImpl () {
this.userNotificationService.updateNotificationSettings(this.user.notificationSettings) this.userNotificationService.updateNotificationSettings(this.user().notificationSettings)
.subscribe({ .subscribe({
next: () => { next: () => {
this.notifier.success($localize`Preferences saved`, undefined, 2000) this.notifier.success($localize`Preferences saved`, undefined, 2000)
@ -159,8 +159,8 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
} }
private loadNotificationSettings () { private loadNotificationSettings () {
for (const key of objectKeysTyped(this.user.notificationSettings)) { for (const key of objectKeysTyped(this.user().notificationSettings)) {
const value = this.user.notificationSettings[key] const value = this.user().notificationSettings[key]
this.emailNotifications[key] = !!(value & UserNotificationSettingValue.EMAIL) this.emailNotifications[key] = !!(value & UserNotificationSettingValue.EMAIL)
this.webNotifications[key] = !!(value & UserNotificationSettingValue.WEB) this.webNotifications[key] = !!(value & UserNotificationSettingValue.WEB)

View File

@ -9,7 +9,7 @@
formControlName="username" readonly formControlName="username" readonly
> >
<div class="form-group-description" i18n> <div class="form-group-description" i18n>
People can find you using &#64;{{ user.username }}&#64;{{ instanceHost }} People can find you using &#64;{{ user().username }}&#64;{{ instanceHost }}
</div> </div>
</div> </div>

View File

@ -1,5 +1,5 @@
import { NgClass, NgIf } from '@angular/common' import { NgClass, NgIf } from '@angular/common'
import { Component, Input, OnInit } from '@angular/core' import { Component, OnInit, inject, input } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { Notifier, User, UserService } from '@app/core' import { Notifier, User, UserService } from '@app/core'
import { USER_DESCRIPTION_VALIDATOR, USER_DISPLAY_NAME_REQUIRED_VALIDATOR } from '@app/shared/form-validators/user-validators' import { USER_DESCRIPTION_VALIDATOR, USER_DISPLAY_NAME_REQUIRED_VALIDATOR } from '@app/shared/form-validators/user-validators'
@ -16,18 +16,14 @@ import { AlertComponent } from '@app/shared/shared-main/common/alert.component'
imports: [ NgIf, FormsModule, ReactiveFormsModule, NgClass, AlertComponent, HelpComponent, MarkdownTextareaComponent ] imports: [ NgIf, FormsModule, ReactiveFormsModule, NgClass, AlertComponent, HelpComponent, MarkdownTextareaComponent ]
}) })
export class MyAccountProfileComponent extends FormReactive implements OnInit { export class MyAccountProfileComponent extends FormReactive implements OnInit {
@Input() user: User = null protected formReactiveService = inject(FormReactiveService)
private notifier = inject(Notifier)
private userService = inject(UserService)
readonly user = input<User>(null)
error: string = null error: string = null
constructor (
protected formReactiveService: FormReactiveService,
private notifier: Notifier,
private userService: UserService
) {
super()
}
ngOnInit () { ngOnInit () {
this.buildForm({ this.buildForm({
'username': null, 'username': null,
@ -37,9 +33,9 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit {
this.form.controls['username'].disable() this.form.controls['username'].disable()
this.form.patchValue({ this.form.patchValue({
'username': this.user.username, 'username': this.user().username,
'display-name': this.user.account.displayName, 'display-name': this.user().account.displayName,
'description': this.user.account.description 'description': this.user().account.description
}) })
} }
@ -54,15 +50,16 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit {
this.error = null this.error = null
this.userService.updateMyProfile({ displayName, description }) this.userService.updateMyProfile({ displayName, description })
.subscribe({ .subscribe({
next: () => { next: () => {
this.user.account.displayName = displayName const user = this.user()
this.user.account.description = description user.account.displayName = displayName
user.account.description = description
this.notifier.success($localize`Profile updated.`) this.notifier.success($localize`Profile updated.`)
}, },
error: err => this.error = err.message error: err => this.error = err.message
}) })
} }
} }

View File

@ -1,6 +1,6 @@
import { ViewportScroller, NgIf } from '@angular/common' import { ViewportScroller, NgIf } from '@angular/common'
import { HttpErrorResponse } from '@angular/common/http' import { HttpErrorResponse } from '@angular/common/http'
import { AfterViewChecked, Component, OnInit } from '@angular/core' import { AfterViewChecked, Component, OnInit, inject } from '@angular/core'
import { AuthService, Notifier, User, UserService } from '@app/core' import { AuthService, Notifier, User, UserService } from '@app/core'
import { genericUploadErrorHandler } from '@app/helpers' import { genericUploadErrorHandler } from '@app/helpers'
import { shallowCopy } from '@peertube/peertube-core-utils' import { shallowCopy } from '@peertube/peertube-core-utils'
@ -38,17 +38,15 @@ import { ActorAvatarEditComponent } from '../../shared/shared-actor-image-edit/a
] ]
}) })
export class MyAccountSettingsComponent implements OnInit, AfterViewChecked { export class MyAccountSettingsComponent implements OnInit, AfterViewChecked {
private viewportScroller = inject(ViewportScroller)
private userService = inject(UserService)
private authService = inject(AuthService)
private notifier = inject(Notifier)
user: User user: User
private lastScrollHash: string private lastScrollHash: string
constructor (
private viewportScroller: ViewportScroller,
private userService: UserService,
private authService: AuthService,
private notifier: Notifier
) {}
get userInformationLoaded () { get userInformationLoaded () {
return this.authService.userInformationLoaded return this.authService.userInformationLoaded
} }
@ -77,11 +75,12 @@ export class MyAccountSettingsComponent implements OnInit, AfterViewChecked {
this.user.account = shallowCopy(this.user.account) this.user.account = shallowCopy(this.user.account)
}, },
error: (err: HttpErrorResponse) => genericUploadErrorHandler({ error: (err: HttpErrorResponse) =>
err, genericUploadErrorHandler({
name: $localize`avatar`, err,
notifier: this.notifier name: $localize`avatar`,
}) notifier: this.notifier
})
}) })
} }

View File

@ -1,5 +1,5 @@
import { NgIf } from '@angular/common' import { NgIf } from '@angular/common'
import { Component, Input, OnInit } from '@angular/core' import { Component, OnInit, inject, input } from '@angular/core'
import { AuthService, ConfirmService, Notifier, User } from '@app/core' import { AuthService, ConfirmService, Notifier, User } from '@app/core'
import { TwoFactorService } from '@app/shared/shared-users/two-factor.service' import { TwoFactorService } from '@app/shared/shared-users/two-factor.service'
import { ButtonComponent } from '../../../shared/shared-main/buttons/button.component' import { ButtonComponent } from '../../../shared/shared-main/buttons/button.component'
@ -10,20 +10,17 @@ import { ButtonComponent } from '../../../shared/shared-main/buttons/button.comp
imports: [ NgIf, ButtonComponent ] imports: [ NgIf, ButtonComponent ]
}) })
export class MyAccountTwoFactorButtonComponent implements OnInit { export class MyAccountTwoFactorButtonComponent implements OnInit {
@Input() user: User private notifier = inject(Notifier)
private twoFactorService = inject(TwoFactorService)
private confirmService = inject(ConfirmService)
private auth = inject(AuthService)
readonly user = input<User>(undefined)
twoFactorEnabled = false twoFactorEnabled = false
constructor (
private notifier: Notifier,
private twoFactorService: TwoFactorService,
private confirmService: ConfirmService,
private auth: AuthService
) {
}
ngOnInit () { ngOnInit () {
this.twoFactorEnabled = this.user.twoFactorEnabled this.twoFactorEnabled = this.user().twoFactorEnabled
} }
async disableTwoFactor () { async disableTwoFactor () {
@ -32,7 +29,7 @@ export class MyAccountTwoFactorButtonComponent implements OnInit {
const { confirmed, password } = await this.confirmService.confirmWithPassword({ message, title: $localize`Disable two factor` }) const { confirmed, password } = await this.confirmService.confirmWithPassword({ message, title: $localize`Disable two factor` })
if (confirmed === false) return if (confirmed === false) return
this.twoFactorService.disableTwoFactor({ userId: this.user.id, currentPassword: password }) this.twoFactorService.disableTwoFactor({ userId: this.user().id, currentPassword: password })
.subscribe({ .subscribe({
next: () => { next: () => {
this.twoFactorEnabled = false this.twoFactorEnabled = false

View File

@ -1,5 +1,5 @@
import { NgIf } from '@angular/common' import { NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { AuthService, Notifier, User } from '@app/core' import { AuthService, Notifier, User } from '@app/core'
@ -16,6 +16,12 @@ import { InputTextComponent } from '../../../shared/shared-forms/input-text.comp
imports: [ NgIf, FormsModule, ReactiveFormsModule, InputTextComponent, QRCodeComponent ] imports: [ NgIf, FormsModule, ReactiveFormsModule, InputTextComponent, QRCodeComponent ]
}) })
export class MyAccountTwoFactorComponent implements OnInit { export class MyAccountTwoFactorComponent implements OnInit {
private notifier = inject(Notifier)
private twoFactorService = inject(TwoFactorService)
private formReactiveService = inject(FormReactiveService)
private auth = inject(AuthService)
private router = inject(Router)
twoFactorAlreadyEnabled: boolean twoFactorAlreadyEnabled: boolean
step: 'request' | 'confirm' | 'confirmed' = 'request' step: 'request' | 'confirm' | 'confirmed' = 'request'
@ -34,15 +40,6 @@ export class MyAccountTwoFactorComponent implements OnInit {
private user: User private user: User
private requestToken: string private requestToken: string
constructor (
private notifier: Notifier,
private twoFactorService: TwoFactorService,
private formReactiveService: FormReactiveService,
private auth: AuthService,
private router: Router
) {
}
ngOnInit () { ngOnInit () {
this.buildPasswordForm() this.buildPasswordForm()
this.buildOTPForm() this.buildOTPForm()

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { RouterOutlet } from '@angular/router' import { RouterOutlet } from '@angular/router'
import { AuthUser, PluginService } from '@app/core' import { AuthUser, PluginService } from '@app/core'
import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared-main/menu/horizontal-menu.component' import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared-main/menu/horizontal-menu.component'
@ -8,13 +8,11 @@ import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared
imports: [ HorizontalMenuComponent, RouterOutlet ] imports: [ HorizontalMenuComponent, RouterOutlet ]
}) })
export class MyAccountComponent implements OnInit { export class MyAccountComponent implements OnInit {
private pluginService = inject(PluginService)
menuEntries: HorizontalMenuEntry[] = [] menuEntries: HorizontalMenuEntry[] = []
user: AuthUser user: AuthUser
constructor (
private pluginService: PluginService
) { }
ngOnInit (): void { ngOnInit (): void {
this.pluginService.ensurePluginsAreLoaded('my-account') this.pluginService.ensurePluginsAreLoaded('my-account')
.then(() => this.buildMenu()) .then(() => this.buildMenu())

View File

@ -1,5 +1,5 @@
import { NgFor, NgIf } from '@angular/common' import { NgFor, NgIf } from '@angular/common'
import { Component } from '@angular/core' import { Component, inject } from '@angular/core'
import { RouterLink } from '@angular/router' import { RouterLink } from '@angular/router'
import { import {
AuthService, AuthService,
@ -28,7 +28,7 @@ import { DeferLoadingDirective } from '../../shared/shared-main/common/defer-loa
import { InfiniteScrollerDirective } from '../../shared/shared-main/common/infinite-scroller.directive' import { InfiniteScrollerDirective } from '../../shared/shared-main/common/infinite-scroller.directive'
import { NumberFormatterPipe } from '../../shared/shared-main/common/number-formatter.pipe' import { NumberFormatterPipe } from '../../shared/shared-main/common/number-formatter.pipe'
type CustomChartData = (ChartData & { startDate: string, total: number }) type CustomChartData = ChartData & { startDate: string, total: number }
@Component({ @Component({
templateUrl: './my-video-channels.component.html', templateUrl: './my-video-channels.component.html',
@ -50,6 +50,12 @@ type CustomChartData = (ChartData & { startDate: string, total: number })
] ]
}) })
export class MyVideoChannelsComponent { export class MyVideoChannelsComponent {
private authService = inject(AuthService)
private notifier = inject(Notifier)
private confirmService = inject(ConfirmService)
private videoChannelService = inject(VideoChannelService)
private screenService = inject(ScreenService)
videoChannels: VideoChannel[] = [] videoChannels: VideoChannel[] = []
videoChannelsChartData: CustomChartData[] videoChannelsChartData: CustomChartData[]
@ -68,14 +74,6 @@ export class MyVideoChannelsComponent {
private pagesDone = new Set<number>() private pagesDone = new Set<number>()
constructor (
private authService: AuthService,
private notifier: Notifier,
private confirmService: ConfirmService,
private videoChannelService: VideoChannelService,
private screenService: ScreenService
) {}
get isInSmallView () { get isInSmallView () {
return this.screenService.isInSmallView() return this.screenService.isInSmallView()
} }
@ -93,17 +91,14 @@ export class MyVideoChannelsComponent {
async deleteVideoChannel (videoChannel: VideoChannel) { async deleteVideoChannel (videoChannel: VideoChannel) {
const res = await this.confirmService.confirmWithExpectedInput( const res = await this.confirmService.confirmWithExpectedInput(
$localize`Do you really want to delete ${videoChannel.displayName}?` + $localize`Do you really want to delete ${videoChannel.displayName}?` +
`<br />` + `<br />` +
formatICU( formatICU(
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
$localize`It will delete {count, plural, =1 {1 video} other {{count} videos}} uploaded in this channel, and you will not be able to create another channel or account with the same name (${videoChannel.name})!`, $localize`It will delete {count, plural, =1 {1 video} other {{count} videos}} uploaded in this channel, and you will not be able to create another channel or account with the same name (${videoChannel.name})!`,
{ count: videoChannel.videosCount } { count: videoChannel.videosCount }
), ),
$localize`Please type the name of the video channel (${videoChannel.name}) to confirm`, $localize`Please type the name of the video channel (${videoChannel.name}) to confirm`,
videoChannel.name, videoChannel.name,
$localize`Delete` $localize`Delete`
) )
if (res === false) return if (res === false) return

View File

@ -1,11 +1,10 @@
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { GlobalIconComponent } from '../../shared/shared-icons/global-icon.component'
import { VideoCommentListAdminOwnerComponent } from '../../shared/shared-video-comment/video-comment-list-admin-owner.component' import { VideoCommentListAdminOwnerComponent } from '../../shared/shared-video-comment/video-comment-list-admin-owner.component'
@Component({ @Component({
templateUrl: './comments-on-my-videos.component.html', templateUrl: './comments-on-my-videos.component.html',
imports: [ imports: [
GlobalIconComponent,
VideoCommentListAdminOwnerComponent VideoCommentListAdminOwnerComponent
] ]
}) })

Some files were not shown because too many files have changed in this diff Show More