diff options
author | Rigel Kent <sendmemail@rigelk.eu> | 2020-02-28 13:52:21 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-28 13:52:21 +0100 |
commit | d3217560a611b94f888ecf3de93b428a7521d4de (patch) | |
tree | 26fc984f351afb994dc13c94e138476ded50c76a /client/src/app/shared | |
parent | 64645512b08875c18ebdc009a550cdfa69def955 (diff) | |
download | PeerTube-d3217560a611b94f888ecf3de93b428a7521d4de.tar.gz PeerTube-d3217560a611b94f888ecf3de93b428a7521d4de.tar.zst PeerTube-d3217560a611b94f888ecf3de93b428a7521d4de.zip |
Add visitor settings, rework logged-in dropdown (#2514)
* Add visitor settings, rework logged-in dropdown
* Make user dropdown P2P switch functional
* Fix lint
* Fix unnecessary notification when user logs out
* Simplify visitor settings code and remove unnecessary icons
* Catch parsing errors and reindent menu styles
Diffstat (limited to 'client/src/app/shared')
-rw-r--r-- | client/src/app/shared/images/global-icon.component.ts | 7 | ||||
-rw-r--r-- | client/src/app/shared/misc/help.component.scss | 1 | ||||
-rw-r--r-- | client/src/app/shared/misc/storage.service.ts | 40 | ||||
-rw-r--r-- | client/src/app/shared/shared.module.ts | 19 | ||||
-rw-r--r-- | client/src/app/shared/users/user.model.ts | 29 | ||||
-rw-r--r-- | client/src/app/shared/users/user.service.ts | 77 | ||||
-rw-r--r-- | client/src/app/shared/video/abstract-video-list.ts | 23 | ||||
-rw-r--r-- | client/src/app/shared/video/video.service.ts | 23 | ||||
-rw-r--r-- | client/src/app/shared/video/videos-selection.component.ts | 4 |
9 files changed, 199 insertions, 24 deletions
diff --git a/client/src/app/shared/images/global-icon.component.ts b/client/src/app/shared/images/global-icon.component.ts index b6e641228..e83daf077 100644 --- a/client/src/app/shared/images/global-icon.component.ts +++ b/client/src/app/shared/images/global-icon.component.ts | |||
@@ -39,15 +39,18 @@ const icons = { | |||
39 | 'playlist-add': require('!!raw-loader?!../../../assets/images/video/playlist-add.svg'), | 39 | 'playlist-add': require('!!raw-loader?!../../../assets/images/video/playlist-add.svg'), |
40 | 'play': require('!!raw-loader?!../../../assets/images/global/play.svg'), | 40 | 'play': require('!!raw-loader?!../../../assets/images/global/play.svg'), |
41 | 'playlists': require('!!raw-loader?!../../../assets/images/global/playlists.svg'), | 41 | 'playlists': require('!!raw-loader?!../../../assets/images/global/playlists.svg'), |
42 | 'about': require('!!raw-loader?!../../../assets/images/menu/about.svg'), | ||
43 | 'globe': require('!!raw-loader?!../../../assets/images/menu/globe.svg'), | 42 | 'globe': require('!!raw-loader?!../../../assets/images/menu/globe.svg'), |
44 | 'home': require('!!raw-loader?!../../../assets/images/menu/home.svg'), | 43 | 'home': require('!!raw-loader?!../../../assets/images/menu/home.svg'), |
45 | 'recently-added': require('!!raw-loader?!../../../assets/images/menu/recently-added.svg'), | 44 | 'recently-added': require('!!raw-loader?!../../../assets/images/menu/recently-added.svg'), |
46 | 'trending': require('!!raw-loader?!../../../assets/images/menu/trending.svg'), | 45 | 'trending': require('!!raw-loader?!../../../assets/images/menu/trending.svg'), |
46 | 'video-lang': require('!!raw-loader?!../../../assets/images/global/video-lang.svg'), | ||
47 | 'videos': require('!!raw-loader?!../../../assets/images/global/videos.svg'), | 47 | 'videos': require('!!raw-loader?!../../../assets/images/global/videos.svg'), |
48 | 'folder': require('!!raw-loader?!../../../assets/images/global/folder.svg'), | 48 | 'folder': require('!!raw-loader?!../../../assets/images/global/folder.svg'), |
49 | 'administration': require('!!raw-loader?!../../../assets/images/menu/administration.svg'), | ||
50 | 'subscriptions': require('!!raw-loader?!../../../assets/images/menu/subscriptions.svg'), | 49 | 'subscriptions': require('!!raw-loader?!../../../assets/images/menu/subscriptions.svg'), |
50 | 'language': require('!!raw-loader?!../../../assets/images/menu/language.svg'), | ||
51 | 'unsensitive': require('!!raw-loader?!../../../assets/images/menu/eye.svg'), | ||
52 | 'sensitive': require('!!raw-loader?!../../../assets/images/menu/eye-closed.svg'), | ||
53 | 'p2p': require('!!raw-loader?!../../../assets/images/menu/p2p.svg'), | ||
51 | 'users': require('!!raw-loader?!../../../assets/images/global/users.svg'), | 54 | 'users': require('!!raw-loader?!../../../assets/images/global/users.svg'), |
52 | 'search': require('!!raw-loader?!../../../assets/images/global/search.svg'), | 55 | 'search': require('!!raw-loader?!../../../assets/images/global/search.svg'), |
53 | 'refresh': require('!!raw-loader?!../../../assets/images/global/refresh.svg') | 56 | 'refresh': require('!!raw-loader?!../../../assets/images/global/refresh.svg') |
diff --git a/client/src/app/shared/misc/help.component.scss b/client/src/app/shared/misc/help.component.scss index f55a516e4..f00df88b5 100644 --- a/client/src/app/shared/misc/help.component.scss +++ b/client/src/app/shared/misc/help.component.scss | |||
@@ -17,6 +17,7 @@ | |||
17 | 17 | ||
18 | ::ng-deep { | 18 | ::ng-deep { |
19 | .help-popover { | 19 | .help-popover { |
20 | z-index: z(help-popover) !important; | ||
20 | max-width: 300px; | 21 | max-width: 300px; |
21 | 22 | ||
22 | .popover-body { | 23 | .popover-body { |
diff --git a/client/src/app/shared/misc/storage.service.ts b/client/src/app/shared/misc/storage.service.ts new file mode 100644 index 000000000..0d4a8ab53 --- /dev/null +++ b/client/src/app/shared/misc/storage.service.ts | |||
@@ -0,0 +1,40 @@ | |||
1 | import { Injectable } from '@angular/core' | ||
2 | import { Observable, Subject } from 'rxjs' | ||
3 | import { | ||
4 | peertubeLocalStorage, | ||
5 | peertubeSessionStorage | ||
6 | } from './peertube-web-storage' | ||
7 | import { filter } from 'rxjs/operators' | ||
8 | |||
9 | abstract class StorageService { | ||
10 | protected instance: Storage | ||
11 | static storageSub = new Subject<string>() | ||
12 | |||
13 | watch (keys?: string[]): Observable<string> { | ||
14 | return StorageService.storageSub.asObservable().pipe(filter(val => keys ? keys.includes(val) : true)) | ||
15 | } | ||
16 | |||
17 | getItem (key: string) { | ||
18 | return this.instance.getItem(key) | ||
19 | } | ||
20 | |||
21 | setItem (key: string, data: any, notifyOfUpdate = true) { | ||
22 | this.instance.setItem(key, data) | ||
23 | if (notifyOfUpdate) StorageService.storageSub.next(key) | ||
24 | } | ||
25 | |||
26 | removeItem (key: string, notifyOfUpdate = true) { | ||
27 | this.instance.removeItem(key) | ||
28 | if (notifyOfUpdate) StorageService.storageSub.next(key) | ||
29 | } | ||
30 | } | ||
31 | |||
32 | @Injectable() | ||
33 | export class LocalStorageService extends StorageService { | ||
34 | protected instance: Storage = peertubeLocalStorage | ||
35 | } | ||
36 | |||
37 | @Injectable() | ||
38 | export class SessionStorageService extends StorageService { | ||
39 | protected instance: Storage = peertubeSessionStorage | ||
40 | } | ||
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 30b3ba0c1..75aa30dab 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts | |||
@@ -47,6 +47,7 @@ import { | |||
47 | import { I18nPrimengCalendarService } from '@app/shared/i18n/i18n-primeng-calendar' | 47 | import { I18nPrimengCalendarService } from '@app/shared/i18n/i18n-primeng-calendar' |
48 | import { InputMaskModule } from 'primeng/inputmask' | 48 | import { InputMaskModule } from 'primeng/inputmask' |
49 | import { ScreenService } from '@app/shared/misc/screen.service' | 49 | import { ScreenService } from '@app/shared/misc/screen.service' |
50 | import { LocalStorageService, SessionStorageService } from '@app/shared/misc/storage.service' | ||
50 | import { VideoCaptionsValidatorsService } from '@app/shared/forms/form-validators/video-captions-validators.service' | 51 | import { VideoCaptionsValidatorsService } from '@app/shared/forms/form-validators/video-captions-validators.service' |
51 | import { VideoCaptionService } from '@app/shared/video-caption' | 52 | import { VideoCaptionService } from '@app/shared/video-caption' |
52 | import { PeertubeCheckboxComponent } from '@app/shared/forms/peertube-checkbox.component' | 53 | import { PeertubeCheckboxComponent } from '@app/shared/forms/peertube-checkbox.component' |
@@ -101,6 +102,10 @@ import { FeatureBooleanComponent } from '@app/shared/instance/feature-boolean.co | |||
101 | import { InputReadonlyCopyComponent } from '@app/shared/forms/input-readonly-copy.component' | 102 | import { InputReadonlyCopyComponent } from '@app/shared/forms/input-readonly-copy.component' |
102 | import { RedundancyService } from '@app/shared/video/redundancy.service' | 103 | import { RedundancyService } from '@app/shared/video/redundancy.service' |
103 | import { ClipboardModule } from '@angular/cdk/clipboard' | 104 | import { ClipboardModule } from '@angular/cdk/clipboard' |
105 | import { InputSwitchModule } from 'primeng/inputswitch' | ||
106 | |||
107 | import { MyAccountVideoSettingsComponent } from '@app/+my-account/my-account-settings/my-account-video-settings' | ||
108 | import { MyAccountInterfaceSettingsComponent } from '@app/+my-account/my-account-settings/my-account-interface' | ||
104 | 109 | ||
105 | @NgModule({ | 110 | @NgModule({ |
106 | imports: [ | 111 | imports: [ |
@@ -122,7 +127,8 @@ import { ClipboardModule } from '@angular/cdk/clipboard' | |||
122 | PrimeSharedModule, | 127 | PrimeSharedModule, |
123 | InputMaskModule, | 128 | InputMaskModule, |
124 | NgPipesModule, | 129 | NgPipesModule, |
125 | MultiSelectModule | 130 | MultiSelectModule, |
131 | InputSwitchModule | ||
126 | ], | 132 | ], |
127 | 133 | ||
128 | declarations: [ | 134 | declarations: [ |
@@ -180,7 +186,10 @@ import { ClipboardModule } from '@angular/cdk/clipboard' | |||
180 | DateToggleComponent, | 186 | DateToggleComponent, |
181 | 187 | ||
182 | GlobalIconComponent, | 188 | GlobalIconComponent, |
183 | PreviewUploadComponent | 189 | PreviewUploadComponent, |
190 | |||
191 | MyAccountVideoSettingsComponent, | ||
192 | MyAccountInterfaceSettingsComponent | ||
184 | ], | 193 | ], |
185 | 194 | ||
186 | exports: [ | 195 | exports: [ |
@@ -258,7 +267,10 @@ import { ClipboardModule } from '@angular/cdk/clipboard' | |||
258 | FromNowPipe, | 267 | FromNowPipe, |
259 | HighlightPipe, | 268 | HighlightPipe, |
260 | PeerTubeTemplateDirective, | 269 | PeerTubeTemplateDirective, |
261 | VideoDurationPipe | 270 | VideoDurationPipe, |
271 | |||
272 | MyAccountVideoSettingsComponent, | ||
273 | MyAccountInterfaceSettingsComponent | ||
262 | ], | 274 | ], |
263 | 275 | ||
264 | providers: [ | 276 | providers: [ |
@@ -303,6 +315,7 @@ import { ClipboardModule } from '@angular/cdk/clipboard' | |||
303 | 315 | ||
304 | I18nPrimengCalendarService, | 316 | I18nPrimengCalendarService, |
305 | ScreenService, | 317 | ScreenService, |
318 | LocalStorageService, SessionStorageService, | ||
306 | 319 | ||
307 | UserNotificationService, | 320 | UserNotificationService, |
308 | 321 | ||
diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts index 7707d7dda..a37cae749 100644 --- a/client/src/app/shared/users/user.model.ts +++ b/client/src/app/shared/users/user.model.ts | |||
@@ -1,10 +1,32 @@ | |||
1 | import { hasUserRight, User as UserServerModel, UserNotificationSetting, UserRight, UserRole, VideoChannel } from '../../../../../shared' | 1 | import { |
2 | hasUserRight, | ||
3 | User as UserServerModel, | ||
4 | UserNotificationSetting, | ||
5 | UserRight, | ||
6 | UserRole | ||
7 | } from '../../../../../shared/models/users' | ||
8 | import { VideoChannel } from '../../../../../shared/models/videos' | ||
2 | import { NSFWPolicyType } from '../../../../../shared/models/videos/nsfw-policy.type' | 9 | import { NSFWPolicyType } from '../../../../../shared/models/videos/nsfw-policy.type' |
3 | import { Account } from '@app/shared/account/account.model' | 10 | import { Account } from '@app/shared/account/account.model' |
4 | import { Avatar } from '../../../../../shared/models/avatars/avatar.model' | 11 | import { Avatar } from '../../../../../shared/models/avatars/avatar.model' |
5 | import { UserAdminFlag } from '@shared/models/users/user-flag.model' | 12 | import { UserAdminFlag } from '@shared/models/users/user-flag.model' |
6 | 13 | ||
7 | export class User implements UserServerModel { | 14 | export class User implements UserServerModel { |
15 | static KEYS = { | ||
16 | ID: 'id', | ||
17 | ROLE: 'role', | ||
18 | EMAIL: 'email', | ||
19 | VIDEOS_HISTORY_ENABLED: 'videos-history-enabled', | ||
20 | USERNAME: 'username', | ||
21 | NSFW_POLICY: 'nsfw_policy', | ||
22 | WEBTORRENT_ENABLED: 'peertube-videojs-' + 'webtorrent_enabled', | ||
23 | AUTO_PLAY_VIDEO: 'auto_play_video', | ||
24 | SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO: 'auto_play_next_video', | ||
25 | AUTO_PLAY_VIDEO_PLAYLIST: 'auto_play_video_playlist', | ||
26 | THEME: 'last_active_theme', | ||
27 | VIDEO_LANGUAGES: 'video_languages' | ||
28 | } | ||
29 | |||
8 | id: number | 30 | id: number |
9 | username: string | 31 | username: string |
10 | email: string | 32 | email: string |
@@ -60,8 +82,11 @@ export class User implements UserServerModel { | |||
60 | 82 | ||
61 | this.nsfwPolicy = hash.nsfwPolicy | 83 | this.nsfwPolicy = hash.nsfwPolicy |
62 | this.webTorrentEnabled = hash.webTorrentEnabled | 84 | this.webTorrentEnabled = hash.webTorrentEnabled |
63 | this.videosHistoryEnabled = hash.videosHistoryEnabled | ||
64 | this.autoPlayVideo = hash.autoPlayVideo | 85 | this.autoPlayVideo = hash.autoPlayVideo |
86 | this.autoPlayNextVideo = hash.autoPlayNextVideo | ||
87 | this.autoPlayNextVideoPlaylist = hash.autoPlayNextVideoPlaylist | ||
88 | this.videosHistoryEnabled = hash.videosHistoryEnabled | ||
89 | this.videoLanguages = hash.videoLanguages | ||
65 | 90 | ||
66 | this.theme = hash.theme | 91 | this.theme = hash.theme |
67 | 92 | ||
diff --git a/client/src/app/shared/users/user.service.ts b/client/src/app/shared/users/user.service.ts index e24d91df3..a79343646 100644 --- a/client/src/app/shared/users/user.service.ts +++ b/client/src/app/shared/users/user.service.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import { from, Observable, of } from 'rxjs' | 1 | import { from, Observable } from 'rxjs' |
2 | import { catchError, concatMap, map, share, shareReplay, tap, toArray } from 'rxjs/operators' | 2 | import { catchError, concatMap, map, shareReplay, toArray } from 'rxjs/operators' |
3 | import { HttpClient, HttpParams } from '@angular/common/http' | 3 | import { HttpClient, HttpParams } from '@angular/common/http' |
4 | import { Injectable } from '@angular/core' | 4 | import { Injectable } from '@angular/core' |
5 | import { ResultList, User, UserCreate, UserRole, UserUpdate, UserUpdateMe, UserVideoQuota } from '../../../../../shared' | 5 | import { ResultList, User as UserServerModel, UserCreate, UserRole, UserUpdate, UserUpdateMe, UserVideoQuota } from '../../../../../shared' |
6 | import { environment } from '../../../environments/environment' | 6 | import { environment } from '../../../environments/environment' |
7 | import { RestExtractor, RestPagination, RestService } from '../rest' | 7 | import { RestExtractor, RestPagination, RestService } from '../rest' |
8 | import { Avatar } from '../../../../../shared/models/avatars/avatar.model' | 8 | import { Avatar } from '../../../../../shared/models/avatars/avatar.model' |
@@ -10,6 +10,10 @@ import { SortMeta } from 'primeng/api' | |||
10 | import { BytesPipe } from 'ngx-pipes' | 10 | import { BytesPipe } from 'ngx-pipes' |
11 | import { I18n } from '@ngx-translate/i18n-polyfill' | 11 | import { I18n } from '@ngx-translate/i18n-polyfill' |
12 | import { UserRegister } from '@shared/models/users/user-register.model' | 12 | import { UserRegister } from '@shared/models/users/user-register.model' |
13 | import { User } from './user.model' | ||
14 | import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type' | ||
15 | import { has } from 'lodash-es' | ||
16 | import { LocalStorageService, SessionStorageService } from '../misc/storage.service' | ||
13 | 17 | ||
14 | @Injectable() | 18 | @Injectable() |
15 | export class UserService { | 19 | export class UserService { |
@@ -17,12 +21,14 @@ export class UserService { | |||
17 | 21 | ||
18 | private bytesPipe = new BytesPipe() | 22 | private bytesPipe = new BytesPipe() |
19 | 23 | ||
20 | private userCache: { [ id: number ]: Observable<User> } = {} | 24 | private userCache: { [ id: number ]: Observable<UserServerModel> } = {} |
21 | 25 | ||
22 | constructor ( | 26 | constructor ( |
23 | private authHttp: HttpClient, | 27 | private authHttp: HttpClient, |
24 | private restExtractor: RestExtractor, | 28 | private restExtractor: RestExtractor, |
25 | private restService: RestService, | 29 | private restService: RestService, |
30 | private localStorageService: LocalStorageService, | ||
31 | private sessionStorageService: SessionStorageService, | ||
26 | private i18n: I18n | 32 | private i18n: I18n |
27 | ) { } | 33 | ) { } |
28 | 34 | ||
@@ -64,6 +70,30 @@ export class UserService { | |||
64 | ) | 70 | ) |
65 | } | 71 | } |
66 | 72 | ||
73 | updateMyAnonymousProfile (profile: UserUpdateMe) { | ||
74 | const supportedKeys = { | ||
75 | // local storage keys | ||
76 | nsfwPolicy: (val: NSFWPolicyType) => this.localStorageService.setItem(User.KEYS.NSFW_POLICY, val), | ||
77 | webTorrentEnabled: (val: boolean) => this.localStorageService.setItem(User.KEYS.WEBTORRENT_ENABLED, String(val)), | ||
78 | autoPlayVideo: (val: boolean) => this.localStorageService.setItem(User.KEYS.AUTO_PLAY_VIDEO, String(val)), | ||
79 | autoPlayNextVideoPlaylist: (val: boolean) => this.localStorageService.setItem(User.KEYS.AUTO_PLAY_VIDEO_PLAYLIST, String(val)), | ||
80 | theme: (val: string) => this.localStorageService.setItem(User.KEYS.THEME, val), | ||
81 | videoLanguages: (val: string[]) => this.localStorageService.setItem(User.KEYS.VIDEO_LANGUAGES, JSON.stringify(val)), | ||
82 | |||
83 | // session storage keys | ||
84 | autoPlayNextVideo: (val: boolean) => | ||
85 | this.sessionStorageService.setItem(User.KEYS.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO, String(val)) | ||
86 | } | ||
87 | |||
88 | for (const key of Object.keys(profile)) { | ||
89 | try { | ||
90 | if (has(supportedKeys, key)) supportedKeys[key](profile[key]) | ||
91 | } catch (err) { | ||
92 | console.error(`Cannot set item ${key} in localStorage. Likely due to a value impossible to stringify.`, err) | ||
93 | } | ||
94 | } | ||
95 | } | ||
96 | |||
67 | deleteMe () { | 97 | deleteMe () { |
68 | const url = UserService.BASE_USERS_URL + 'me' | 98 | const url = UserService.BASE_USERS_URL + 'me' |
69 | 99 | ||
@@ -187,7 +217,7 @@ export class UserService { | |||
187 | ) | 217 | ) |
188 | } | 218 | } |
189 | 219 | ||
190 | updateUsers (users: User[], userUpdate: UserUpdate) { | 220 | updateUsers (users: UserServerModel[], userUpdate: UserUpdate) { |
191 | return from(users) | 221 | return from(users) |
192 | .pipe( | 222 | .pipe( |
193 | concatMap(u => this.authHttp.put(UserService.BASE_USERS_URL + u.id, userUpdate)), | 223 | concatMap(u => this.authHttp.put(UserService.BASE_USERS_URL + u.id, userUpdate)), |
@@ -205,17 +235,40 @@ export class UserService { | |||
205 | } | 235 | } |
206 | 236 | ||
207 | getUser (userId: number) { | 237 | getUser (userId: number) { |
208 | return this.authHttp.get<User>(UserService.BASE_USERS_URL + userId) | 238 | return this.authHttp.get<UserServerModel>(UserService.BASE_USERS_URL + userId) |
209 | .pipe(catchError(err => this.restExtractor.handleError(err))) | 239 | .pipe(catchError(err => this.restExtractor.handleError(err))) |
210 | } | 240 | } |
211 | 241 | ||
212 | getUsers (pagination: RestPagination, sort: SortMeta, search?: string): Observable<ResultList<User>> { | 242 | getAnonymousUser () { |
243 | let videoLanguages | ||
244 | try { | ||
245 | videoLanguages = JSON.parse(this.localStorageService.getItem(User.KEYS.VIDEO_LANGUAGES)) | ||
246 | } catch (err) { | ||
247 | videoLanguages = null | ||
248 | console.error('Cannot parse desired video languages from localStorage.', err) | ||
249 | } | ||
250 | |||
251 | return new User({ | ||
252 | // local storage keys | ||
253 | nsfwPolicy: this.localStorageService.getItem(User.KEYS.NSFW_POLICY) as NSFWPolicyType, | ||
254 | webTorrentEnabled: this.localStorageService.getItem(User.KEYS.WEBTORRENT_ENABLED) !== 'false', | ||
255 | autoPlayVideo: this.localStorageService.getItem(User.KEYS.AUTO_PLAY_VIDEO) === 'true', | ||
256 | autoPlayNextVideoPlaylist: this.localStorageService.getItem(User.KEYS.AUTO_PLAY_VIDEO_PLAYLIST) === 'true', | ||
257 | theme: this.localStorageService.getItem(User.KEYS.THEME) || 'default', | ||
258 | videoLanguages, | ||
259 | |||
260 | // session storage keys | ||
261 | autoPlayNextVideo: this.sessionStorageService.getItem(User.KEYS.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO) === 'true' | ||
262 | }) | ||
263 | } | ||
264 | |||
265 | getUsers (pagination: RestPagination, sort: SortMeta, search?: string): Observable<ResultList<UserServerModel>> { | ||
213 | let params = new HttpParams() | 266 | let params = new HttpParams() |
214 | params = this.restService.addRestGetParams(params, pagination, sort) | 267 | params = this.restService.addRestGetParams(params, pagination, sort) |
215 | 268 | ||
216 | if (search) params = params.append('search', search) | 269 | if (search) params = params.append('search', search) |
217 | 270 | ||
218 | return this.authHttp.get<ResultList<User>>(UserService.BASE_USERS_URL, { params }) | 271 | return this.authHttp.get<ResultList<UserServerModel>>(UserService.BASE_USERS_URL, { params }) |
219 | .pipe( | 272 | .pipe( |
220 | map(res => this.restExtractor.convertResultListDateToHuman(res)), | 273 | map(res => this.restExtractor.convertResultListDateToHuman(res)), |
221 | map(res => this.restExtractor.applyToResultListData(res, this.formatUser.bind(this))), | 274 | map(res => this.restExtractor.applyToResultListData(res, this.formatUser.bind(this))), |
@@ -223,7 +276,7 @@ export class UserService { | |||
223 | ) | 276 | ) |
224 | } | 277 | } |
225 | 278 | ||
226 | removeUser (usersArg: User | User[]) { | 279 | removeUser (usersArg: UserServerModel | UserServerModel[]) { |
227 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] | 280 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] |
228 | 281 | ||
229 | return from(users) | 282 | return from(users) |
@@ -234,7 +287,7 @@ export class UserService { | |||
234 | ) | 287 | ) |
235 | } | 288 | } |
236 | 289 | ||
237 | banUsers (usersArg: User | User[], reason?: string) { | 290 | banUsers (usersArg: UserServerModel | UserServerModel[], reason?: string) { |
238 | const body = reason ? { reason } : {} | 291 | const body = reason ? { reason } : {} |
239 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] | 292 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] |
240 | 293 | ||
@@ -246,7 +299,7 @@ export class UserService { | |||
246 | ) | 299 | ) |
247 | } | 300 | } |
248 | 301 | ||
249 | unbanUsers (usersArg: User | User[]) { | 302 | unbanUsers (usersArg: UserServerModel | UserServerModel[]) { |
250 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] | 303 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] |
251 | 304 | ||
252 | return from(users) | 305 | return from(users) |
@@ -257,7 +310,7 @@ export class UserService { | |||
257 | ) | 310 | ) |
258 | } | 311 | } |
259 | 312 | ||
260 | private formatUser (user: User) { | 313 | private formatUser (user: UserServerModel) { |
261 | let videoQuota | 314 | let videoQuota |
262 | if (user.videoQuota === -1) { | 315 | if (user.videoQuota === -1) { |
263 | videoQuota = this.i18n('Unlimited') | 316 | videoQuota = this.i18n('Unlimited') |
diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts index 2f5f82aa3..b146d7014 100644 --- a/client/src/app/shared/video/abstract-video-list.ts +++ b/client/src/app/shared/video/abstract-video-list.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { debounceTime, first, tap } from 'rxjs/operators' | 1 | import { debounceTime, first, tap, throttleTime } from 'rxjs/operators' |
2 | import { OnDestroy, OnInit } from '@angular/core' | 2 | import { OnDestroy, OnInit } from '@angular/core' |
3 | import { ActivatedRoute, Router } from '@angular/router' | 3 | import { ActivatedRoute, Router } from '@angular/router' |
4 | import { fromEvent, Observable, of, Subject, Subscription } from 'rxjs' | 4 | import { fromEvent, Observable, of, Subject, Subscription } from 'rxjs' |
@@ -15,6 +15,8 @@ import { I18n } from '@ngx-translate/i18n-polyfill' | |||
15 | import { isLastMonth, isLastWeek, isToday, isYesterday } from '@shared/core-utils/miscs/date' | 15 | import { isLastMonth, isLastWeek, isToday, isYesterday } from '@shared/core-utils/miscs/date' |
16 | import { ServerConfig } from '@shared/models' | 16 | import { ServerConfig } from '@shared/models' |
17 | import { GlobalIconName } from '@app/shared/images/global-icon.component' | 17 | import { GlobalIconName } from '@app/shared/images/global-icon.component' |
18 | import { UserService, User } from '../users' | ||
19 | import { LocalStorageService } from '../misc/storage.service' | ||
18 | 20 | ||
19 | enum GroupDate { | 21 | enum GroupDate { |
20 | UNKNOWN = 0, | 22 | UNKNOWN = 0, |
@@ -72,9 +74,11 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor | |||
72 | 74 | ||
73 | protected abstract notifier: Notifier | 75 | protected abstract notifier: Notifier |
74 | protected abstract authService: AuthService | 76 | protected abstract authService: AuthService |
77 | protected abstract userService: UserService | ||
75 | protected abstract route: ActivatedRoute | 78 | protected abstract route: ActivatedRoute |
76 | protected abstract serverService: ServerService | 79 | protected abstract serverService: ServerService |
77 | protected abstract screenService: ScreenService | 80 | protected abstract screenService: ScreenService |
81 | protected abstract storageService: LocalStorageService | ||
78 | protected abstract router: Router | 82 | protected abstract router: Router |
79 | protected abstract i18n: I18n | 83 | protected abstract i18n: I18n |
80 | abstract titlePage: string | 84 | abstract titlePage: string |
@@ -124,6 +128,16 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor | |||
124 | if (this.loadOnInit === true) { | 128 | if (this.loadOnInit === true) { |
125 | loadUserObservable.subscribe(() => this.loadMoreVideos()) | 129 | loadUserObservable.subscribe(() => this.loadMoreVideos()) |
126 | } | 130 | } |
131 | |||
132 | this.storageService.watch([ | ||
133 | User.KEYS.NSFW_POLICY, | ||
134 | User.KEYS.VIDEO_LANGUAGES | ||
135 | ]).pipe(throttleTime(200)).subscribe( | ||
136 | () => { | ||
137 | this.loadUserVideoLanguagesIfNeeded() | ||
138 | if (this.hasDoneFirstQuery) this.reloadVideos() | ||
139 | } | ||
140 | ) | ||
127 | } | 141 | } |
128 | 142 | ||
129 | ngOnDestroy () { | 143 | ngOnDestroy () { |
@@ -279,7 +293,12 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor | |||
279 | } | 293 | } |
280 | 294 | ||
281 | private loadUserVideoLanguagesIfNeeded () { | 295 | private loadUserVideoLanguagesIfNeeded () { |
282 | if (!this.authService.isLoggedIn() || !this.useUserVideoLanguagePreferences) { | 296 | if (!this.useUserVideoLanguagePreferences) { |
297 | return of(true) | ||
298 | } | ||
299 | |||
300 | if (!this.authService.isLoggedIn()) { | ||
301 | this.languageOneOf = this.userService.getAnonymousUser().videoLanguages | ||
283 | return of(true) | 302 | return of(true) |
284 | } | 303 | } |
285 | 304 | ||
diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts index 996202154..a51b9cab9 100644 --- a/client/src/app/shared/video/video.service.ts +++ b/client/src/app/shared/video/video.service.ts | |||
@@ -27,10 +27,11 @@ import { objectToFormData } from '@app/shared/misc/utils' | |||
27 | import { Account } from '@app/shared/account/account.model' | 27 | import { Account } from '@app/shared/account/account.model' |
28 | import { AccountService } from '@app/shared/account/account.service' | 28 | import { AccountService } from '@app/shared/account/account.service' |
29 | import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' | 29 | import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' |
30 | import { ServerService } from '@app/core' | 30 | import { ServerService, AuthService } from '@app/core' |
31 | import { UserSubscriptionService } from '@app/shared/user-subscription/user-subscription.service' | 31 | import { UserSubscriptionService } from '@app/shared/user-subscription/user-subscription.service' |
32 | import { VideoChannel } from '@app/shared/video-channel/video-channel.model' | 32 | import { VideoChannel } from '@app/shared/video-channel/video-channel.model' |
33 | import { I18n } from '@ngx-translate/i18n-polyfill' | 33 | import { I18n } from '@ngx-translate/i18n-polyfill' |
34 | import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type' | ||
34 | 35 | ||
35 | export interface VideosProvider { | 36 | export interface VideosProvider { |
36 | getVideos (parameters: { | 37 | getVideos (parameters: { |
@@ -49,6 +50,8 @@ export class VideoService implements VideosProvider { | |||
49 | 50 | ||
50 | constructor ( | 51 | constructor ( |
51 | private authHttp: HttpClient, | 52 | private authHttp: HttpClient, |
53 | private authService: AuthService, | ||
54 | private userService: UserService, | ||
52 | private restExtractor: RestExtractor, | 55 | private restExtractor: RestExtractor, |
53 | private restService: RestService, | 56 | private restService: RestService, |
54 | private serverService: ServerService, | 57 | private serverService: ServerService, |
@@ -199,9 +202,10 @@ export class VideoService implements VideosProvider { | |||
199 | filter?: VideoFilter, | 202 | filter?: VideoFilter, |
200 | categoryOneOf?: number, | 203 | categoryOneOf?: number, |
201 | languageOneOf?: string[], | 204 | languageOneOf?: string[], |
202 | skipCount?: boolean | 205 | skipCount?: boolean, |
206 | nsfw?: boolean | ||
203 | }): Observable<ResultList<Video>> { | 207 | }): Observable<ResultList<Video>> { |
204 | const { videoPagination, sort, filter, categoryOneOf, languageOneOf, skipCount } = parameters | 208 | const { videoPagination, sort, filter, categoryOneOf, languageOneOf, skipCount, nsfw } = parameters |
205 | 209 | ||
206 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | 210 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) |
207 | 211 | ||
@@ -212,6 +216,15 @@ export class VideoService implements VideosProvider { | |||
212 | if (categoryOneOf) params = params.set('categoryOneOf', categoryOneOf + '') | 216 | if (categoryOneOf) params = params.set('categoryOneOf', categoryOneOf + '') |
213 | if (skipCount) params = params.set('skipCount', skipCount + '') | 217 | if (skipCount) params = params.set('skipCount', skipCount + '') |
214 | 218 | ||
219 | if (nsfw) { | ||
220 | params = params.set('nsfw', nsfw + '') | ||
221 | } else { | ||
222 | const nsfwPolicy = this.authService.isLoggedIn() | ||
223 | ? this.authService.getUser().nsfwPolicy | ||
224 | : this.userService.getAnonymousUser().nsfwPolicy | ||
225 | if (this.nsfwPolicyToFilter(nsfwPolicy)) params.set('nsfw', 'false') | ||
226 | } | ||
227 | |||
215 | if (languageOneOf) { | 228 | if (languageOneOf) { |
216 | for (const l of languageOneOf) { | 229 | for (const l of languageOneOf) { |
217 | params = params.append('languageOneOf[]', l) | 230 | params = params.append('languageOneOf[]', l) |
@@ -368,4 +381,8 @@ export class VideoService implements VideosProvider { | |||
368 | catchError(err => this.restExtractor.handleError(err)) | 381 | catchError(err => this.restExtractor.handleError(err)) |
369 | ) | 382 | ) |
370 | } | 383 | } |
384 | |||
385 | private nsfwPolicyToFilter (policy: NSFWPolicyType) { | ||
386 | return policy === 'do_not_list' | ||
387 | } | ||
371 | } | 388 | } |
diff --git a/client/src/app/shared/video/videos-selection.component.ts b/client/src/app/shared/video/videos-selection.component.ts index 064420056..17e5beb24 100644 --- a/client/src/app/shared/video/videos-selection.component.ts +++ b/client/src/app/shared/video/videos-selection.component.ts | |||
@@ -22,6 +22,8 @@ import { VideoSortField } from '@app/shared/video/sort-field.type' | |||
22 | import { ComponentPagination } from '@app/shared/rest/component-pagination.model' | 22 | import { ComponentPagination } from '@app/shared/rest/component-pagination.model' |
23 | import { I18n } from '@ngx-translate/i18n-polyfill' | 23 | import { I18n } from '@ngx-translate/i18n-polyfill' |
24 | import { ResultList } from '@shared/models' | 24 | import { ResultList } from '@shared/models' |
25 | import { UserService } from '../users' | ||
26 | import { LocalStorageService } from '../misc/storage.service' | ||
25 | 27 | ||
26 | export type SelectionType = { [ id: number ]: boolean } | 28 | export type SelectionType = { [ id: number ]: boolean } |
27 | 29 | ||
@@ -51,7 +53,9 @@ export class VideosSelectionComponent extends AbstractVideoList implements OnIni | |||
51 | protected route: ActivatedRoute, | 53 | protected route: ActivatedRoute, |
52 | protected notifier: Notifier, | 54 | protected notifier: Notifier, |
53 | protected authService: AuthService, | 55 | protected authService: AuthService, |
56 | protected userService: UserService, | ||
54 | protected screenService: ScreenService, | 57 | protected screenService: ScreenService, |
58 | protected storageService: LocalStorageService, | ||
55 | protected serverService: ServerService | 59 | protected serverService: ServerService |
56 | ) { | 60 | ) { |
57 | super() | 61 | super() |