diff options
Diffstat (limited to 'client/src/app/core')
-rw-r--r-- | client/src/app/core/auth/auth-user.model.ts | 14 | ||||
-rw-r--r-- | client/src/app/core/auth/auth.service.ts | 18 | ||||
-rw-r--r-- | client/src/app/core/auth/index.ts | 1 | ||||
-rw-r--r-- | client/src/app/core/confirm/confirm.component.html | 25 | ||||
-rw-r--r-- | client/src/app/core/confirm/confirm.component.scss | 17 | ||||
-rw-r--r-- | client/src/app/core/confirm/confirm.component.ts | 68 | ||||
-rw-r--r-- | client/src/app/core/confirm/index.ts | 1 | ||||
-rw-r--r-- | client/src/app/core/core.module.ts | 25 | ||||
-rw-r--r-- | client/src/app/core/index.ts | 1 | ||||
-rw-r--r-- | client/src/app/core/notification/index.ts | 2 | ||||
-rw-r--r-- | client/src/app/core/notification/notifier.service.ts | 41 | ||||
-rw-r--r-- | client/src/app/core/notification/user-notification-socket.service.ts | 41 | ||||
-rw-r--r-- | client/src/app/core/routing/login-guard.service.ts | 10 | ||||
-rw-r--r-- | client/src/app/core/routing/redirect.service.ts | 21 | ||||
-rw-r--r-- | client/src/app/core/routing/user-right-guard.service.ts | 2 | ||||
-rw-r--r-- | client/src/app/core/server/server.service.ts | 23 | ||||
-rw-r--r-- | client/src/app/core/theme/theme.service.ts | 4 |
17 files changed, 158 insertions, 156 deletions
diff --git a/client/src/app/core/auth/auth-user.model.ts b/client/src/app/core/auth/auth-user.model.ts index 74ed1c580..abb11fdc2 100644 --- a/client/src/app/core/auth/auth-user.model.ts +++ b/client/src/app/core/auth/auth-user.model.ts | |||
@@ -1,8 +1,9 @@ | |||
1 | import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' | 1 | import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' |
2 | import { UserRight } from '../../../../../shared/models/users/user-right.enum' | 2 | import { UserRight } from '../../../../../shared/models/users/user-right.enum' |
3 | import { User as ServerUserModel } from '../../../../../shared/models/users/user.model' | ||
3 | // Do not use the barrel (dependency loop) | 4 | // Do not use the barrel (dependency loop) |
4 | import { hasUserRight, UserRole } from '../../../../../shared/models/users/user-role' | 5 | import { hasUserRight, UserRole } from '../../../../../shared/models/users/user-role' |
5 | import { User, UserConstructorHash } from '../../shared/users/user.model' | 6 | import { User } from '../../shared/users/user.model' |
6 | import { NSFWPolicyType } from '../../../../../shared/models/videos/nsfw-policy.type' | 7 | import { NSFWPolicyType } from '../../../../../shared/models/videos/nsfw-policy.type' |
7 | 8 | ||
8 | export type TokenOptions = { | 9 | export type TokenOptions = { |
@@ -70,8 +71,10 @@ export class AuthUser extends User { | |||
70 | ID: 'id', | 71 | ID: 'id', |
71 | ROLE: 'role', | 72 | ROLE: 'role', |
72 | EMAIL: 'email', | 73 | EMAIL: 'email', |
74 | VIDEOS_HISTORY_ENABLED: 'videos-history-enabled', | ||
73 | USERNAME: 'username', | 75 | USERNAME: 'username', |
74 | NSFW_POLICY: 'nsfw_policy', | 76 | NSFW_POLICY: 'nsfw_policy', |
77 | WEBTORRENT_ENABLED: 'peertube-videojs-' + 'webtorrent_enabled', | ||
75 | AUTO_PLAY_VIDEO: 'auto_play_video' | 78 | AUTO_PLAY_VIDEO: 'auto_play_video' |
76 | } | 79 | } |
77 | 80 | ||
@@ -87,7 +90,9 @@ export class AuthUser extends User { | |||
87 | email: peertubeLocalStorage.getItem(this.KEYS.EMAIL), | 90 | email: peertubeLocalStorage.getItem(this.KEYS.EMAIL), |
88 | role: parseInt(peertubeLocalStorage.getItem(this.KEYS.ROLE), 10) as UserRole, | 91 | role: parseInt(peertubeLocalStorage.getItem(this.KEYS.ROLE), 10) as UserRole, |
89 | nsfwPolicy: peertubeLocalStorage.getItem(this.KEYS.NSFW_POLICY) as NSFWPolicyType, | 92 | nsfwPolicy: peertubeLocalStorage.getItem(this.KEYS.NSFW_POLICY) as NSFWPolicyType, |
90 | autoPlayVideo: peertubeLocalStorage.getItem(this.KEYS.AUTO_PLAY_VIDEO) === 'true' | 93 | webTorrentEnabled: peertubeLocalStorage.getItem(this.KEYS.WEBTORRENT_ENABLED) === 'true', |
94 | autoPlayVideo: peertubeLocalStorage.getItem(this.KEYS.AUTO_PLAY_VIDEO) === 'true', | ||
95 | videosHistoryEnabled: peertubeLocalStorage.getItem(this.KEYS.VIDEOS_HISTORY_ENABLED) === 'true' | ||
91 | }, | 96 | }, |
92 | Tokens.load() | 97 | Tokens.load() |
93 | ) | 98 | ) |
@@ -101,12 +106,14 @@ export class AuthUser extends User { | |||
101 | peertubeLocalStorage.removeItem(this.KEYS.ID) | 106 | peertubeLocalStorage.removeItem(this.KEYS.ID) |
102 | peertubeLocalStorage.removeItem(this.KEYS.ROLE) | 107 | peertubeLocalStorage.removeItem(this.KEYS.ROLE) |
103 | peertubeLocalStorage.removeItem(this.KEYS.NSFW_POLICY) | 108 | peertubeLocalStorage.removeItem(this.KEYS.NSFW_POLICY) |
109 | peertubeLocalStorage.removeItem(this.KEYS.WEBTORRENT_ENABLED) | ||
110 | peertubeLocalStorage.removeItem(this.KEYS.VIDEOS_HISTORY_ENABLED) | ||
104 | peertubeLocalStorage.removeItem(this.KEYS.AUTO_PLAY_VIDEO) | 111 | peertubeLocalStorage.removeItem(this.KEYS.AUTO_PLAY_VIDEO) |
105 | peertubeLocalStorage.removeItem(this.KEYS.EMAIL) | 112 | peertubeLocalStorage.removeItem(this.KEYS.EMAIL) |
106 | Tokens.flush() | 113 | Tokens.flush() |
107 | } | 114 | } |
108 | 115 | ||
109 | constructor (userHash: UserConstructorHash, hashTokens: TokenOptions) { | 116 | constructor (userHash: Partial<ServerUserModel>, hashTokens: TokenOptions) { |
110 | super(userHash) | 117 | super(userHash) |
111 | this.tokens = new Tokens(hashTokens) | 118 | this.tokens = new Tokens(hashTokens) |
112 | } | 119 | } |
@@ -138,6 +145,7 @@ export class AuthUser extends User { | |||
138 | peertubeLocalStorage.setItem(AuthUser.KEYS.EMAIL, this.email) | 145 | peertubeLocalStorage.setItem(AuthUser.KEYS.EMAIL, this.email) |
139 | peertubeLocalStorage.setItem(AuthUser.KEYS.ROLE, this.role.toString()) | 146 | peertubeLocalStorage.setItem(AuthUser.KEYS.ROLE, this.role.toString()) |
140 | peertubeLocalStorage.setItem(AuthUser.KEYS.NSFW_POLICY, this.nsfwPolicy.toString()) | 147 | peertubeLocalStorage.setItem(AuthUser.KEYS.NSFW_POLICY, this.nsfwPolicy.toString()) |
148 | peertubeLocalStorage.setItem(AuthUser.KEYS.WEBTORRENT_ENABLED, JSON.stringify(this.webTorrentEnabled)) | ||
141 | peertubeLocalStorage.setItem(AuthUser.KEYS.AUTO_PLAY_VIDEO, JSON.stringify(this.autoPlayVideo)) | 149 | peertubeLocalStorage.setItem(AuthUser.KEYS.AUTO_PLAY_VIDEO, JSON.stringify(this.autoPlayVideo)) |
142 | this.tokens.save() | 150 | this.tokens.save() |
143 | } | 151 | } |
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index 9c36b946e..eaa822e0f 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts | |||
@@ -3,18 +3,18 @@ import { catchError, map, mergeMap, share, tap } from 'rxjs/operators' | |||
3 | import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http' | 3 | import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http' |
4 | import { Injectable } from '@angular/core' | 4 | import { Injectable } from '@angular/core' |
5 | import { Router } from '@angular/router' | 5 | import { Router } from '@angular/router' |
6 | import { NotificationsService } from 'angular2-notifications' | 6 | import { Notifier } from '@app/core/notification/notifier.service' |
7 | import { OAuthClientLocal, User as UserServerModel, UserRefreshToken } from '../../../../../shared' | 7 | import { OAuthClientLocal, User as UserServerModel, UserRefreshToken } from '../../../../../shared' |
8 | import { User } from '../../../../../shared/models/users' | 8 | import { User } from '../../../../../shared/models/users' |
9 | import { UserLogin } from '../../../../../shared/models/users/user-login.model' | 9 | import { UserLogin } from '../../../../../shared/models/users/user-login.model' |
10 | import { environment } from '../../../environments/environment' | 10 | import { environment } from '../../../environments/environment' |
11 | import { RestExtractor } from '../../shared/rest' | 11 | import { RestExtractor } from '../../shared/rest/rest-extractor.service' |
12 | import { AuthStatus } from './auth-status.model' | 12 | import { AuthStatus } from './auth-status.model' |
13 | import { AuthUser } from './auth-user.model' | 13 | import { AuthUser } from './auth-user.model' |
14 | import { objectToUrlEncoded } from '@app/shared/misc/utils' | 14 | import { objectToUrlEncoded } from '@app/shared/misc/utils' |
15 | import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' | 15 | import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' |
16 | import { I18n } from '@ngx-translate/i18n-polyfill' | 16 | import { I18n } from '@ngx-translate/i18n-polyfill' |
17 | import { HotkeysService, Hotkey } from 'angular2-hotkeys' | 17 | import { Hotkey, HotkeysService } from 'angular2-hotkeys' |
18 | 18 | ||
19 | interface UserLoginWithUsername extends UserLogin { | 19 | interface UserLoginWithUsername extends UserLogin { |
20 | access_token: string | 20 | access_token: string |
@@ -38,7 +38,6 @@ export class AuthService { | |||
38 | loginChangedSource: Observable<AuthStatus> | 38 | loginChangedSource: Observable<AuthStatus> |
39 | userInformationLoaded = new ReplaySubject<boolean>(1) | 39 | userInformationLoaded = new ReplaySubject<boolean>(1) |
40 | hotkeys: Hotkey[] | 40 | hotkeys: Hotkey[] |
41 | redirectUrl: string | ||
42 | 41 | ||
43 | private clientId: string = peertubeLocalStorage.getItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID) | 42 | private clientId: string = peertubeLocalStorage.getItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID) |
44 | private clientSecret: string = peertubeLocalStorage.getItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_SECRET) | 43 | private clientSecret: string = peertubeLocalStorage.getItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_SECRET) |
@@ -48,7 +47,7 @@ export class AuthService { | |||
48 | 47 | ||
49 | constructor ( | 48 | constructor ( |
50 | private http: HttpClient, | 49 | private http: HttpClient, |
51 | private notificationsService: NotificationsService, | 50 | private notifier: Notifier, |
52 | private hotkeysService: HotkeysService, | 51 | private hotkeysService: HotkeysService, |
53 | private restExtractor: RestExtractor, | 52 | private restExtractor: RestExtractor, |
54 | private router: Router, | 53 | private router: Router, |
@@ -106,9 +105,8 @@ export class AuthService { | |||
106 | ) | 105 | ) |
107 | } | 106 | } |
108 | 107 | ||
109 | // We put a bigger timeout | 108 | // We put a bigger timeout: this is an important message |
110 | // This is an important message | 109 | this.notifier.error(errorMessage, this.i18n('Error'), 7000) |
111 | this.notificationsService.error(this.i18n('Error'), errorMessage, { timeOut: 7000 }) | ||
112 | } | 110 | } |
113 | ) | 111 | ) |
114 | } | 112 | } |
@@ -178,8 +176,6 @@ export class AuthService { | |||
178 | this.setStatus(AuthStatus.LoggedOut) | 176 | this.setStatus(AuthStatus.LoggedOut) |
179 | 177 | ||
180 | this.hotkeysService.remove(this.hotkeys) | 178 | this.hotkeysService.remove(this.hotkeys) |
181 | |||
182 | this.redirectUrl = null | ||
183 | } | 179 | } |
184 | 180 | ||
185 | refreshAccessToken () { | 181 | refreshAccessToken () { |
@@ -221,7 +217,7 @@ export class AuthService { | |||
221 | } | 217 | } |
222 | 218 | ||
223 | refreshUserInformation () { | 219 | refreshUserInformation () { |
224 | const obj = { | 220 | const obj: UserLoginWithUsername = { |
225 | access_token: this.user.getAccessToken(), | 221 | access_token: this.user.getAccessToken(), |
226 | refresh_token: null, | 222 | refresh_token: null, |
227 | token_type: this.user.getTokenType(), | 223 | token_type: this.user.getTokenType(), |
diff --git a/client/src/app/core/auth/index.ts b/client/src/app/core/auth/index.ts index bc7bfec0e..8e5caa7ed 100644 --- a/client/src/app/core/auth/index.ts +++ b/client/src/app/core/auth/index.ts | |||
@@ -1,4 +1,3 @@ | |||
1 | export * from './auth-status.model' | 1 | export * from './auth-status.model' |
2 | export * from './auth-user.model' | 2 | export * from './auth-user.model' |
3 | export * from './auth.service' | 3 | export * from './auth.service' |
4 | export * from '../routing/login-guard.service' | ||
diff --git a/client/src/app/core/confirm/confirm.component.html b/client/src/app/core/confirm/confirm.component.html deleted file mode 100644 index 43f0c6190..000000000 --- a/client/src/app/core/confirm/confirm.component.html +++ /dev/null | |||
@@ -1,25 +0,0 @@ | |||
1 | <ng-template #confirmModal let-close="close" let-dismiss="dismiss"> | ||
2 | |||
3 | <div class="modal-header"> | ||
4 | <h4 class="modal-title">{{ title }}</h4> | ||
5 | <span class="close" aria-label="Close" role="button" (click)="dismiss()"></span> | ||
6 | </div> | ||
7 | |||
8 | <div class="modal-body" > | ||
9 | <div [innerHtml]="message"></div> | ||
10 | |||
11 | <div *ngIf="inputLabel && expectedInputValue" class="form-group"> | ||
12 | <label for="confirmInput">{{ inputLabel }}</label> | ||
13 | <input type="text" id="confirmInput" name="confirmInput" [(ngModel)]="inputValue" /> | ||
14 | </div> | ||
15 | </div> | ||
16 | |||
17 | <div class="modal-footer inputs"> | ||
18 | <span i18n class="action-button action-button-cancel" (click)="dismiss()" role="button">Cancel</span> | ||
19 | |||
20 | <input | ||
21 | type="submit" [value]="confirmButtonText" class="action-button-submit" [disabled]="isConfirmationDisabled()" | ||
22 | (click)="close()" | ||
23 | > | ||
24 | </div> | ||
25 | </ng-template> | ||
diff --git a/client/src/app/core/confirm/confirm.component.scss b/client/src/app/core/confirm/confirm.component.scss deleted file mode 100644 index 93dd7926b..000000000 --- a/client/src/app/core/confirm/confirm.component.scss +++ /dev/null | |||
@@ -1,17 +0,0 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .button { | ||
5 | padding: 0 13px; | ||
6 | } | ||
7 | |||
8 | input[type=text] { | ||
9 | @include peertube-input-text(100%); | ||
10 | display: block; | ||
11 | } | ||
12 | |||
13 | .form-group { | ||
14 | margin: 20px 0; | ||
15 | } | ||
16 | |||
17 | |||
diff --git a/client/src/app/core/confirm/confirm.component.ts b/client/src/app/core/confirm/confirm.component.ts deleted file mode 100644 index 5138b7848..000000000 --- a/client/src/app/core/confirm/confirm.component.ts +++ /dev/null | |||
@@ -1,68 +0,0 @@ | |||
1 | import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core' | ||
2 | import { ConfirmService } from './confirm.service' | ||
3 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
4 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | ||
5 | import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' | ||
6 | |||
7 | @Component({ | ||
8 | selector: 'my-confirm', | ||
9 | templateUrl: './confirm.component.html', | ||
10 | styleUrls: [ './confirm.component.scss' ] | ||
11 | }) | ||
12 | export class ConfirmComponent implements OnInit { | ||
13 | @ViewChild('confirmModal') confirmModal: ElementRef | ||
14 | |||
15 | title = '' | ||
16 | message = '' | ||
17 | expectedInputValue = '' | ||
18 | inputLabel = '' | ||
19 | |||
20 | inputValue = '' | ||
21 | confirmButtonText = '' | ||
22 | |||
23 | private openedModal: NgbModalRef | ||
24 | |||
25 | constructor ( | ||
26 | private modalService: NgbModal, | ||
27 | private confirmService: ConfirmService, | ||
28 | private i18n: I18n | ||
29 | ) { } | ||
30 | |||
31 | ngOnInit () { | ||
32 | this.confirmService.showConfirm.subscribe( | ||
33 | ({ title, message, expectedInputValue, inputLabel, confirmButtonText }) => { | ||
34 | this.title = title | ||
35 | this.message = message | ||
36 | |||
37 | this.inputLabel = inputLabel | ||
38 | this.expectedInputValue = expectedInputValue | ||
39 | |||
40 | this.confirmButtonText = confirmButtonText || this.i18n('Confirm') | ||
41 | |||
42 | this.showModal() | ||
43 | } | ||
44 | ) | ||
45 | } | ||
46 | |||
47 | @HostListener('document:keydown.enter') | ||
48 | confirm () { | ||
49 | if (this.openedModal) this.openedModal.close() | ||
50 | } | ||
51 | |||
52 | isConfirmationDisabled () { | ||
53 | // No input validation | ||
54 | if (!this.inputLabel || !this.expectedInputValue) return false | ||
55 | |||
56 | return this.expectedInputValue !== this.inputValue | ||
57 | } | ||
58 | |||
59 | showModal () { | ||
60 | this.inputValue = '' | ||
61 | |||
62 | this.openedModal = this.modalService.open(this.confirmModal) | ||
63 | |||
64 | this.openedModal.result | ||
65 | .then(() => this.confirmService.confirmResponse.next(true)) | ||
66 | .catch(() => this.confirmService.confirmResponse.next(false)) | ||
67 | } | ||
68 | } | ||
diff --git a/client/src/app/core/confirm/index.ts b/client/src/app/core/confirm/index.ts index 44aabfc13..aca591e1a 100644 --- a/client/src/app/core/confirm/index.ts +++ b/client/src/app/core/confirm/index.ts | |||
@@ -1,2 +1 @@ | |||
1 | export * from './confirm.component' | ||
2 | export * from './confirm.service' | export * from './confirm.service' | |
diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts index df2ec696d..4ef3b1e73 100644 --- a/client/src/app/core/core.module.ts +++ b/client/src/app/core/core.module.ts | |||
@@ -7,16 +7,18 @@ import { LoadingBarModule } from '@ngx-loading-bar/core' | |||
7 | import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client' | 7 | import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client' |
8 | import { LoadingBarRouterModule } from '@ngx-loading-bar/router' | 8 | import { LoadingBarRouterModule } from '@ngx-loading-bar/router' |
9 | 9 | ||
10 | import { SimpleNotificationsModule } from 'angular2-notifications' | ||
11 | |||
12 | import { AuthService } from './auth' | 10 | import { AuthService } from './auth' |
13 | import { ConfirmComponent, ConfirmService } from './confirm' | 11 | import { ConfirmService } from './confirm' |
14 | import { throwIfAlreadyLoaded } from './module-import-guard' | 12 | import { throwIfAlreadyLoaded } from './module-import-guard' |
15 | import { LoginGuard, RedirectService, UserRightGuard } from './routing' | 13 | import { LoginGuard, RedirectService, UserRightGuard } from './routing' |
16 | import { ServerService } from './server' | 14 | import { ServerService } from './server' |
17 | import { ThemeService } from './theme' | 15 | import { ThemeService } from './theme' |
18 | import { HotkeyModule } from 'angular2-hotkeys' | 16 | import { HotkeyModule } from 'angular2-hotkeys' |
19 | import { CheatSheetComponent } from '@app/core/hotkeys' | 17 | import { CheatSheetComponent } from './hotkeys' |
18 | import { ToastModule } from 'primeng/toast' | ||
19 | import { Notifier } from './notification' | ||
20 | import { MessageService } from 'primeng/api' | ||
21 | import { UserNotificationSocket } from '@app/core/notification/user-notification-socket.service' | ||
20 | 22 | ||
21 | @NgModule({ | 23 | @NgModule({ |
22 | imports: [ | 24 | imports: [ |
@@ -25,11 +27,10 @@ import { CheatSheetComponent } from '@app/core/hotkeys' | |||
25 | FormsModule, | 27 | FormsModule, |
26 | BrowserAnimationsModule, | 28 | BrowserAnimationsModule, |
27 | 29 | ||
28 | SimpleNotificationsModule.forRoot(), | ||
29 | |||
30 | LoadingBarHttpClientModule, | 30 | LoadingBarHttpClientModule, |
31 | LoadingBarRouterModule, | 31 | LoadingBarRouterModule, |
32 | LoadingBarModule.forRoot(), | 32 | LoadingBarModule, |
33 | ToastModule, | ||
33 | 34 | ||
34 | HotkeyModule.forRoot({ | 35 | HotkeyModule.forRoot({ |
35 | cheatSheetCloseEsc: true | 36 | cheatSheetCloseEsc: true |
@@ -37,16 +38,15 @@ import { CheatSheetComponent } from '@app/core/hotkeys' | |||
37 | ], | 38 | ], |
38 | 39 | ||
39 | declarations: [ | 40 | declarations: [ |
40 | ConfirmComponent, | ||
41 | CheatSheetComponent | 41 | CheatSheetComponent |
42 | ], | 42 | ], |
43 | 43 | ||
44 | exports: [ | 44 | exports: [ |
45 | SimpleNotificationsModule, | ||
46 | LoadingBarHttpClientModule, | 45 | LoadingBarHttpClientModule, |
47 | LoadingBarModule, | 46 | LoadingBarModule, |
48 | 47 | ||
49 | ConfirmComponent, | 48 | ToastModule, |
49 | |||
50 | CheatSheetComponent | 50 | CheatSheetComponent |
51 | ], | 51 | ], |
52 | 52 | ||
@@ -57,7 +57,10 @@ import { CheatSheetComponent } from '@app/core/hotkeys' | |||
57 | ThemeService, | 57 | ThemeService, |
58 | LoginGuard, | 58 | LoginGuard, |
59 | UserRightGuard, | 59 | UserRightGuard, |
60 | RedirectService | 60 | RedirectService, |
61 | Notifier, | ||
62 | MessageService, | ||
63 | UserNotificationSocket | ||
61 | ] | 64 | ] |
62 | }) | 65 | }) |
63 | export class CoreModule { | 66 | export class CoreModule { |
diff --git a/client/src/app/core/index.ts b/client/src/app/core/index.ts index 524589d74..f664aff41 100644 --- a/client/src/app/core/index.ts +++ b/client/src/app/core/index.ts | |||
@@ -2,6 +2,7 @@ export * from './auth' | |||
2 | export * from './confirm' | 2 | export * from './confirm' |
3 | export * from './routing' | 3 | export * from './routing' |
4 | export * from './server' | 4 | export * from './server' |
5 | export * from './notification' | ||
5 | export * from './theme' | 6 | export * from './theme' |
6 | 7 | ||
7 | export * from './core.module' | 8 | export * from './core.module' |
diff --git a/client/src/app/core/notification/index.ts b/client/src/app/core/notification/index.ts new file mode 100644 index 000000000..3e8d9ea65 --- /dev/null +++ b/client/src/app/core/notification/index.ts | |||
@@ -0,0 +1,2 @@ | |||
1 | export * from './notifier.service' | ||
2 | export * from './user-notification-socket.service' | ||
diff --git a/client/src/app/core/notification/notifier.service.ts b/client/src/app/core/notification/notifier.service.ts new file mode 100644 index 000000000..9833c65a0 --- /dev/null +++ b/client/src/app/core/notification/notifier.service.ts | |||
@@ -0,0 +1,41 @@ | |||
1 | import { Injectable } from '@angular/core' | ||
2 | import { MessageService } from 'primeng/api' | ||
3 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
4 | |||
5 | @Injectable() | ||
6 | export class Notifier { | ||
7 | readonly TIMEOUT = 5000 | ||
8 | |||
9 | constructor ( | ||
10 | private i18n: I18n, | ||
11 | private messageService: MessageService) { | ||
12 | } | ||
13 | |||
14 | info (text: string, title?: string, timeout?: number) { | ||
15 | if (!title) title = this.i18n('Info') | ||
16 | |||
17 | return this.notify('info', text, title, timeout) | ||
18 | } | ||
19 | |||
20 | error (text: string, title?: string, timeout?: number) { | ||
21 | if (!title) title = this.i18n('Error') | ||
22 | |||
23 | return this.notify('error', text, title, timeout) | ||
24 | } | ||
25 | |||
26 | success (text: string, title?: string, timeout?: number) { | ||
27 | if (!title) title = this.i18n('Success') | ||
28 | |||
29 | return this.notify('success', text, title, timeout) | ||
30 | } | ||
31 | |||
32 | private notify (severity: 'success' | 'info' | 'warn' | 'error', text: string, title: string, timeout?: number) { | ||
33 | this.messageService.add({ | ||
34 | severity, | ||
35 | summary: title, | ||
36 | detail: text, | ||
37 | closable: true, | ||
38 | life: timeout || this.TIMEOUT | ||
39 | }) | ||
40 | } | ||
41 | } | ||
diff --git a/client/src/app/core/notification/user-notification-socket.service.ts b/client/src/app/core/notification/user-notification-socket.service.ts new file mode 100644 index 000000000..f367d9ae4 --- /dev/null +++ b/client/src/app/core/notification/user-notification-socket.service.ts | |||
@@ -0,0 +1,41 @@ | |||
1 | import { Injectable } from '@angular/core' | ||
2 | import { environment } from '../../../environments/environment' | ||
3 | import { UserNotification as UserNotificationServer } from '../../../../../shared' | ||
4 | import { Subject } from 'rxjs' | ||
5 | import * as io from 'socket.io-client' | ||
6 | import { AuthService } from '../auth' | ||
7 | |||
8 | export type NotificationEvent = 'new' | 'read' | 'read-all' | ||
9 | |||
10 | @Injectable() | ||
11 | export class UserNotificationSocket { | ||
12 | private notificationSubject = new Subject<{ type: NotificationEvent, notification?: UserNotificationServer }>() | ||
13 | |||
14 | private socket: SocketIOClient.Socket | ||
15 | |||
16 | constructor ( | ||
17 | private auth: AuthService | ||
18 | ) {} | ||
19 | |||
20 | dispatch (type: NotificationEvent, notification?: UserNotificationServer) { | ||
21 | this.notificationSubject.next({ type, notification }) | ||
22 | } | ||
23 | |||
24 | getMyNotificationsSocket () { | ||
25 | const socket = this.getSocket() | ||
26 | |||
27 | socket.on('new-notification', (n: UserNotificationServer) => this.dispatch('new', n)) | ||
28 | |||
29 | return this.notificationSubject.asObservable() | ||
30 | } | ||
31 | |||
32 | private getSocket () { | ||
33 | if (this.socket) return this.socket | ||
34 | |||
35 | this.socket = io(environment.apiUrl + '/user-notifications', { | ||
36 | query: { accessToken: this.auth.getAccessToken() } | ||
37 | }) | ||
38 | |||
39 | return this.socket | ||
40 | } | ||
41 | } | ||
diff --git a/client/src/app/core/routing/login-guard.service.ts b/client/src/app/core/routing/login-guard.service.ts index 40ff8f505..7b1c37ee8 100644 --- a/client/src/app/core/routing/login-guard.service.ts +++ b/client/src/app/core/routing/login-guard.service.ts | |||
@@ -1,11 +1,5 @@ | |||
1 | import { Injectable } from '@angular/core' | 1 | import { Injectable } from '@angular/core' |
2 | import { | 2 | import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot } from '@angular/router' |
3 | ActivatedRouteSnapshot, | ||
4 | CanActivateChild, | ||
5 | RouterStateSnapshot, | ||
6 | CanActivate, | ||
7 | Router | ||
8 | } from '@angular/router' | ||
9 | 3 | ||
10 | import { AuthService } from '../auth/auth.service' | 4 | import { AuthService } from '../auth/auth.service' |
11 | 5 | ||
@@ -20,8 +14,6 @@ export class LoginGuard implements CanActivate, CanActivateChild { | |||
20 | canActivate (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { | 14 | canActivate (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { |
21 | if (this.auth.isLoggedIn() === true) return true | 15 | if (this.auth.isLoggedIn() === true) return true |
22 | 16 | ||
23 | this.auth.redirectUrl = state.url | ||
24 | |||
25 | this.router.navigate([ '/login' ]) | 17 | this.router.navigate([ '/login' ]) |
26 | return false | 18 | return false |
27 | } | 19 | } |
diff --git a/client/src/app/core/routing/redirect.service.ts b/client/src/app/core/routing/redirect.service.ts index 1881be117..e1db4097b 100644 --- a/client/src/app/core/routing/redirect.service.ts +++ b/client/src/app/core/routing/redirect.service.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { Injectable } from '@angular/core' | 1 | import { Injectable } from '@angular/core' |
2 | import { Router } from '@angular/router' | 2 | import { NavigationEnd, Router } from '@angular/router' |
3 | import { ServerService } from '../server' | 3 | import { ServerService } from '../server' |
4 | 4 | ||
5 | @Injectable() | 5 | @Injectable() |
@@ -8,6 +8,9 @@ export class RedirectService { | |||
8 | static INIT_DEFAULT_ROUTE = '/videos/trending' | 8 | static INIT_DEFAULT_ROUTE = '/videos/trending' |
9 | static DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE | 9 | static DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE |
10 | 10 | ||
11 | private previousUrl: string | ||
12 | private currentUrl: string | ||
13 | |||
11 | constructor ( | 14 | constructor ( |
12 | private router: Router, | 15 | private router: Router, |
13 | private serverService: ServerService | 16 | private serverService: ServerService |
@@ -18,6 +21,7 @@ export class RedirectService { | |||
18 | RedirectService.DEFAULT_ROUTE = config.instance.defaultClientRoute | 21 | RedirectService.DEFAULT_ROUTE = config.instance.defaultClientRoute |
19 | } | 22 | } |
20 | 23 | ||
24 | // Load default route | ||
21 | this.serverService.configLoaded | 25 | this.serverService.configLoaded |
22 | .subscribe(() => { | 26 | .subscribe(() => { |
23 | const defaultRouteConfig = this.serverService.getConfig().instance.defaultClientRoute | 27 | const defaultRouteConfig = this.serverService.getConfig().instance.defaultClientRoute |
@@ -26,6 +30,21 @@ export class RedirectService { | |||
26 | RedirectService.DEFAULT_ROUTE = defaultRouteConfig | 30 | RedirectService.DEFAULT_ROUTE = defaultRouteConfig |
27 | } | 31 | } |
28 | }) | 32 | }) |
33 | |||
34 | // Track previous url | ||
35 | this.currentUrl = this.router.url | ||
36 | router.events.subscribe(event => { | ||
37 | if (event instanceof NavigationEnd) { | ||
38 | this.previousUrl = this.currentUrl | ||
39 | this.currentUrl = event.url | ||
40 | } | ||
41 | }) | ||
42 | } | ||
43 | |||
44 | redirectToPreviousRoute () { | ||
45 | if (this.previousUrl) return this.router.navigateByUrl(this.previousUrl) | ||
46 | |||
47 | return this.redirectToHomepage() | ||
29 | } | 48 | } |
30 | 49 | ||
31 | redirectToHomepage (skipLocationChange = false) { | 50 | redirectToHomepage (skipLocationChange = false) { |
diff --git a/client/src/app/core/routing/user-right-guard.service.ts b/client/src/app/core/routing/user-right-guard.service.ts index 65d029977..50c3d8c19 100644 --- a/client/src/app/core/routing/user-right-guard.service.ts +++ b/client/src/app/core/routing/user-right-guard.service.ts | |||
@@ -7,7 +7,7 @@ import { | |||
7 | Router | 7 | Router |
8 | } from '@angular/router' | 8 | } from '@angular/router' |
9 | 9 | ||
10 | import { AuthService } from '../auth' | 10 | import { AuthService } from '../auth/auth.service' |
11 | 11 | ||
12 | @Injectable() | 12 | @Injectable() |
13 | export class UserRightGuard implements CanActivate, CanActivateChild { | 13 | export class UserRightGuard implements CanActivate, CanActivateChild { |
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts index 2f1ef1fc2..c868ccdcc 100644 --- a/client/src/app/core/server/server.service.ts +++ b/client/src/app/core/server/server.service.ts | |||
@@ -13,6 +13,7 @@ import { sortBy } from '@app/shared/misc/utils' | |||
13 | 13 | ||
14 | @Injectable() | 14 | @Injectable() |
15 | export class ServerService { | 15 | export class ServerService { |
16 | private static BASE_SERVER_URL = environment.apiUrl + '/api/v1/server/' | ||
16 | private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/' | 17 | private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/' |
17 | private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/' | 18 | private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/' |
18 | private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/' | 19 | private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/' |
@@ -37,6 +38,12 @@ export class ServerService { | |||
37 | css: '' | 38 | css: '' |
38 | } | 39 | } |
39 | }, | 40 | }, |
41 | email: { | ||
42 | enabled: false | ||
43 | }, | ||
44 | contactForm: { | ||
45 | enabled: false | ||
46 | }, | ||
40 | serverVersion: 'Unknown', | 47 | serverVersion: 'Unknown', |
41 | signup: { | 48 | signup: { |
42 | allowed: false, | 49 | allowed: false, |
@@ -44,7 +51,10 @@ export class ServerService { | |||
44 | requiresEmailVerification: false | 51 | requiresEmailVerification: false |
45 | }, | 52 | }, |
46 | transcoding: { | 53 | transcoding: { |
47 | enabledResolutions: [] | 54 | enabledResolutions: [], |
55 | hls: { | ||
56 | enabled: false | ||
57 | } | ||
48 | }, | 58 | }, |
49 | avatar: { | 59 | avatar: { |
50 | file: { | 60 | file: { |
@@ -80,6 +90,11 @@ export class ServerService { | |||
80 | enabled: false | 90 | enabled: false |
81 | } | 91 | } |
82 | } | 92 | } |
93 | }, | ||
94 | trending: { | ||
95 | videos: { | ||
96 | intervalDays: 0 | ||
97 | } | ||
83 | } | 98 | } |
84 | } | 99 | } |
85 | private videoCategories: Array<VideoConstant<number>> = [] | 100 | private videoCategories: Array<VideoConstant<number>> = [] |
@@ -141,10 +156,6 @@ export class ServerService { | |||
141 | return this.videoPrivacies | 156 | return this.videoPrivacies |
142 | } | 157 | } |
143 | 158 | ||
144 | getAbout () { | ||
145 | return this.http.get<About>(ServerService.BASE_CONFIG_URL + '/about') | ||
146 | } | ||
147 | |||
148 | private loadVideoAttributeEnum ( | 159 | private loadVideoAttributeEnum ( |
149 | attributeName: 'categories' | 'licences' | 'languages' | 'privacies', | 160 | attributeName: 'categories' | 'licences' | 'languages' | 'privacies', |
150 | hashToPopulate: VideoConstant<string | number>[], | 161 | hashToPopulate: VideoConstant<string | number>[], |
@@ -154,7 +165,7 @@ export class ServerService { | |||
154 | this.localeObservable | 165 | this.localeObservable |
155 | .pipe( | 166 | .pipe( |
156 | switchMap(translations => { | 167 | switchMap(translations => { |
157 | return this.http.get(ServerService.BASE_VIDEO_URL + attributeName) | 168 | return this.http.get<{ [id: string]: string }>(ServerService.BASE_VIDEO_URL + attributeName) |
158 | .pipe(map(data => ({ data, translations }))) | 169 | .pipe(map(data => ({ data, translations }))) |
159 | }) | 170 | }) |
160 | ) | 171 | ) |
diff --git a/client/src/app/core/theme/theme.service.ts b/client/src/app/core/theme/theme.service.ts index a6eef0898..50c19ecac 100644 --- a/client/src/app/core/theme/theme.service.ts +++ b/client/src/app/core/theme/theme.service.ts | |||
@@ -5,7 +5,7 @@ import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' | |||
5 | export class ThemeService { | 5 | export class ThemeService { |
6 | private theme = document.querySelector('body') | 6 | private theme = document.querySelector('body') |
7 | private darkTheme = false | 7 | private darkTheme = false |
8 | private previousTheme = {} | 8 | private previousTheme: { [ id: string ]: string } = {} |
9 | 9 | ||
10 | constructor () { | 10 | constructor () { |
11 | // initialise the alternative theme with dark theme colors | 11 | // initialise the alternative theme with dark theme colors |
@@ -33,7 +33,7 @@ export class ThemeService { | |||
33 | } | 33 | } |
34 | } | 34 | } |
35 | 35 | ||
36 | private switchProperty (property, newValue?) { | 36 | private switchProperty (property: string, newValue?: string) { |
37 | const propertyOldvalue = window.getComputedStyle(this.theme).getPropertyValue('--' + property) | 37 | const propertyOldvalue = window.getComputedStyle(this.theme).getPropertyValue('--' + property) |
38 | this.theme.style.setProperty('--' + property, (newValue) ? newValue : this.previousTheme[property]) | 38 | this.theme.style.setProperty('--' + property, (newValue) ? newValue : this.previousTheme[property]) |
39 | this.previousTheme[property] = propertyOldvalue | 39 | this.previousTheme[property] = propertyOldvalue |