diff options
Diffstat (limited to 'client/src/app/shared')
7 files changed, 59 insertions, 43 deletions
diff --git a/client/src/app/shared/form-validators/user-validators.ts b/client/src/app/shared/form-validators/user-validators.ts index fee37e95f..976c97b87 100644 --- a/client/src/app/shared/form-validators/user-validators.ts +++ b/client/src/app/shared/form-validators/user-validators.ts | |||
@@ -1,12 +1,14 @@ | |||
1 | import { Validators } from '@angular/forms' | 1 | import { Validators } from '@angular/forms' |
2 | import { BuildFormValidator } from './form-validator.model' | 2 | import { BuildFormValidator } from './form-validator.model' |
3 | 3 | ||
4 | export const USER_USERNAME_REGEX_CHARACTERS = '[a-z0-9][a-z0-9._]' | ||
5 | |||
4 | export const USER_USERNAME_VALIDATOR: BuildFormValidator = { | 6 | export const USER_USERNAME_VALIDATOR: BuildFormValidator = { |
5 | VALIDATORS: [ | 7 | VALIDATORS: [ |
6 | Validators.required, | 8 | Validators.required, |
7 | Validators.minLength(1), | 9 | Validators.minLength(1), |
8 | Validators.maxLength(50), | 10 | Validators.maxLength(50), |
9 | Validators.pattern(/^[a-z0-9][a-z0-9._]*$/) | 11 | Validators.pattern(new RegExp(`^${USER_USERNAME_REGEX_CHARACTERS}*$`)) |
10 | ], | 12 | ], |
11 | MESSAGES: { | 13 | MESSAGES: { |
12 | 'required': $localize`Username is required.`, | 14 | 'required': $localize`Username is required.`, |
diff --git a/client/src/app/shared/shared-main/account/actor.service.ts b/client/src/app/shared/shared-main/account/actor.service.ts deleted file mode 100644 index 464ed4519..000000000 --- a/client/src/app/shared/shared-main/account/actor.service.ts +++ /dev/null | |||
@@ -1,37 +0,0 @@ | |||
1 | import { Observable, ReplaySubject } from 'rxjs' | ||
2 | import { catchError, map, tap } from 'rxjs/operators' | ||
3 | import { HttpClient } from '@angular/common/http' | ||
4 | import { Injectable } from '@angular/core' | ||
5 | import { RestExtractor } from '@app/core' | ||
6 | import { Account as ServerAccount, VideoChannel as ServerVideoChannel } from '@shared/models' | ||
7 | import { environment } from '../../../../environments/environment' | ||
8 | |||
9 | type KeysOfUnion<T> = T extends T ? keyof T: never | ||
10 | type ServerActor = KeysOfUnion<ServerAccount | ServerVideoChannel> | ||
11 | |||
12 | @Injectable() | ||
13 | export class ActorService { | ||
14 | static BASE_ACTOR_API_URL = environment.apiUrl + '/api/v1/actors/' | ||
15 | |||
16 | actorLoaded = new ReplaySubject<string>(1) | ||
17 | |||
18 | constructor ( | ||
19 | private authHttp: HttpClient, | ||
20 | private restExtractor: RestExtractor | ||
21 | ) {} | ||
22 | |||
23 | getActorType (actorName: string): Observable<string> { | ||
24 | return this.authHttp.get<ServerActor>(ActorService.BASE_ACTOR_API_URL + actorName) | ||
25 | .pipe( | ||
26 | map(actorHash => { | ||
27 | if (actorHash[ 'userId' ]) { | ||
28 | return 'Account' | ||
29 | } | ||
30 | |||
31 | return 'VideoChannel' | ||
32 | }), | ||
33 | tap(actor => this.actorLoaded.next(actor)), | ||
34 | catchError(res => this.restExtractor.handleError(res)) | ||
35 | ) | ||
36 | } | ||
37 | } | ||
diff --git a/client/src/app/shared/shared-main/account/index.ts b/client/src/app/shared/shared-main/account/index.ts index c6cdcd574..b80ddb9f5 100644 --- a/client/src/app/shared/shared-main/account/index.ts +++ b/client/src/app/shared/shared-main/account/index.ts | |||
@@ -1,4 +1,3 @@ | |||
1 | export * from './account.model' | 1 | export * from './account.model' |
2 | export * from './account.service' | 2 | export * from './account.service' |
3 | export * from './actor.model' | 3 | export * from './actor.model' |
4 | export * from './actor.service' | ||
diff --git a/client/src/app/shared/shared-main/index.ts b/client/src/app/shared/shared-main/index.ts index a4d813c06..3a7fd4c34 100644 --- a/client/src/app/shared/shared-main/index.ts +++ b/client/src/app/shared/shared-main/index.ts | |||
@@ -5,6 +5,9 @@ export * from './date' | |||
5 | export * from './feeds' | 5 | export * from './feeds' |
6 | export * from './loaders' | 6 | export * from './loaders' |
7 | export * from './misc' | 7 | export * from './misc' |
8 | export * from './peertube-modal' | ||
9 | export * from './plugins' | ||
10 | export * from './router' | ||
8 | export * from './users' | 11 | export * from './users' |
9 | export * from './video' | 12 | export * from './video' |
10 | export * from './video-caption' | 13 | export * from './video-caption' |
diff --git a/client/src/app/shared/shared-main/router/actor-redirect-guard.service.ts b/client/src/app/shared/shared-main/router/actor-redirect-guard.service.ts new file mode 100644 index 000000000..49d61f945 --- /dev/null +++ b/client/src/app/shared/shared-main/router/actor-redirect-guard.service.ts | |||
@@ -0,0 +1,46 @@ | |||
1 | import { forkJoin, of } from 'rxjs' | ||
2 | import { catchError, map } from 'rxjs/operators' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router' | ||
5 | import { AccountService } from '../account' | ||
6 | import { VideoChannelService } from '../video-channel' | ||
7 | |||
8 | @Injectable() | ||
9 | export class ActorRedirectGuard implements CanActivate { | ||
10 | |||
11 | constructor ( | ||
12 | private router: Router, | ||
13 | private accountService: AccountService, | ||
14 | private channelService: VideoChannelService | ||
15 | ) {} | ||
16 | |||
17 | canActivate (route: ActivatedRouteSnapshot) { | ||
18 | const actorName = route.params.actorName | ||
19 | |||
20 | return forkJoin([ | ||
21 | this.accountService.getAccount(actorName).pipe(this.orUndefined()), | ||
22 | this.channelService.getVideoChannel(actorName).pipe(this.orUndefined()) | ||
23 | ]).pipe( | ||
24 | map(([ account, channel ]) => { | ||
25 | if (!account && !channel) { | ||
26 | this.router.navigate([ '/404' ]) | ||
27 | return false | ||
28 | } | ||
29 | |||
30 | if (account) { | ||
31 | this.router.navigate([ `/a/${actorName}` ], { skipLocationChange: true }) | ||
32 | } | ||
33 | |||
34 | if (channel) { | ||
35 | this.router.navigate([ `/c/${actorName}` ], { skipLocationChange: true }) | ||
36 | } | ||
37 | |||
38 | return true | ||
39 | }) | ||
40 | ) | ||
41 | } | ||
42 | |||
43 | private orUndefined () { | ||
44 | return catchError(() => of(undefined)) | ||
45 | } | ||
46 | } | ||
diff --git a/client/src/app/shared/shared-main/router/index.ts b/client/src/app/shared/shared-main/router/index.ts new file mode 100644 index 000000000..f4000b674 --- /dev/null +++ b/client/src/app/shared/shared-main/router/index.ts | |||
@@ -0,0 +1 @@ | |||
export * from './actor-redirect-guard.service' | |||
diff --git a/client/src/app/shared/shared-main/shared-main.module.ts b/client/src/app/shared/shared-main/shared-main.module.ts index f06f25ca5..c8dd01429 100644 --- a/client/src/app/shared/shared-main/shared-main.module.ts +++ b/client/src/app/shared/shared-main/shared-main.module.ts | |||
@@ -4,7 +4,7 @@ import { CommonModule, DatePipe } from '@angular/common' | |||
4 | import { HttpClientModule } from '@angular/common/http' | 4 | import { HttpClientModule } from '@angular/common/http' |
5 | import { NgModule } from '@angular/core' | 5 | import { NgModule } from '@angular/core' |
6 | import { FormsModule, ReactiveFormsModule } from '@angular/forms' | 6 | import { FormsModule, ReactiveFormsModule } from '@angular/forms' |
7 | import { RouterModule } from '@angular/router' | 7 | import { ActivatedRouteSnapshot, RouterModule } from '@angular/router' |
8 | import { | 8 | import { |
9 | NgbButtonsModule, | 9 | NgbButtonsModule, |
10 | NgbCollapseModule, | 10 | NgbCollapseModule, |
@@ -17,7 +17,7 @@ import { | |||
17 | import { LoadingBarModule } from '@ngx-loading-bar/core' | 17 | import { LoadingBarModule } from '@ngx-loading-bar/core' |
18 | import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client' | 18 | import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client' |
19 | import { SharedGlobalIconModule } from '../shared-icons' | 19 | import { SharedGlobalIconModule } from '../shared-icons' |
20 | import { AccountService, ActorService } from './account' | 20 | import { AccountService } from './account' |
21 | import { | 21 | import { |
22 | AutofocusDirective, | 22 | AutofocusDirective, |
23 | BytesPipe, | 23 | BytesPipe, |
@@ -39,6 +39,7 @@ import { UserHistoryService, UserNotificationsComponent, UserNotificationService | |||
39 | import { RedundancyService, VideoImportService, VideoOwnershipService, VideoService } from './video' | 39 | import { RedundancyService, VideoImportService, VideoOwnershipService, VideoService } from './video' |
40 | import { VideoCaptionService } from './video-caption' | 40 | import { VideoCaptionService } from './video-caption' |
41 | import { VideoChannelService } from './video-channel' | 41 | import { VideoChannelService } from './video-channel' |
42 | import { ActorRedirectGuard } from './router' | ||
42 | 43 | ||
43 | @NgModule({ | 44 | @NgModule({ |
44 | imports: [ | 45 | imports: [ |
@@ -161,7 +162,6 @@ import { VideoChannelService } from './video-channel' | |||
161 | AUTH_INTERCEPTOR_PROVIDER, | 162 | AUTH_INTERCEPTOR_PROVIDER, |
162 | 163 | ||
163 | AccountService, | 164 | AccountService, |
164 | ActorService, | ||
165 | 165 | ||
166 | UserHistoryService, | 166 | UserHistoryService, |
167 | UserNotificationService, | 167 | UserNotificationService, |
@@ -175,7 +175,9 @@ import { VideoChannelService } from './video-channel' | |||
175 | 175 | ||
176 | VideoChannelService, | 176 | VideoChannelService, |
177 | 177 | ||
178 | CustomPageService | 178 | CustomPageService, |
179 | |||
180 | ActorRedirectGuard | ||
179 | ] | 181 | ] |
180 | }) | 182 | }) |
181 | export class SharedMainModule { } | 183 | export class SharedMainModule { } |