diff options
25 files changed, 320 insertions, 194 deletions
diff --git a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts index 0048882bc..c29f69475 100644 --- a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts +++ b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts | |||
@@ -2,7 +2,7 @@ import { BytesPipe } from 'ngx-pipes' | |||
2 | import { SortMeta } from 'primeng/api' | 2 | import { SortMeta } from 'primeng/api' |
3 | import { Component, OnInit } from '@angular/core' | 3 | import { Component, OnInit } from '@angular/core' |
4 | import { ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' | 4 | import { ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' |
5 | import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage' | 5 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' |
6 | import { RedundancyService } from '@app/shared/shared-main' | 6 | import { RedundancyService } from '@app/shared/shared-main' |
7 | import { I18n } from '@ngx-translate/i18n-polyfill' | 7 | import { I18n } from '@ngx-translate/i18n-polyfill' |
8 | import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models' | 8 | import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models' |
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts index e94c293ca..3242bcf46 100644 --- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts +++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts | |||
@@ -14,7 +14,7 @@ import { DomSanitizer } from '@angular/platform-browser' | |||
14 | @Component({ | 14 | @Component({ |
15 | selector: 'my-video-block-list', | 15 | selector: 'my-video-block-list', |
16 | templateUrl: './video-block-list.component.html', | 16 | templateUrl: './video-block-list.component.html', |
17 | styleUrls: [ '../../../shared/shared-moderation/moderation.scss', '../../../shared/shared-abuse-list/abuse-list-table.component.scss', './video-block-list.component.scss' ] | 17 | styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-block-list.component.scss' ] |
18 | }) | 18 | }) |
19 | export class VideoBlockListComponent extends RestTable implements OnInit, AfterViewInit { | 19 | export class VideoBlockListComponent extends RestTable implements OnInit, AfterViewInit { |
20 | blocklist: (VideoBlacklist & { reasonHtml?: string, embedHtml?: string })[] = [] | 20 | blocklist: (VideoBlacklist & { reasonHtml?: string, embedHtml?: string })[] = [] |
diff --git a/client/src/app/+admin/system/jobs/jobs.component.ts b/client/src/app/+admin/system/jobs/jobs.component.ts index 38a78de33..ceb848976 100644 --- a/client/src/app/+admin/system/jobs/jobs.component.ts +++ b/client/src/app/+admin/system/jobs/jobs.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { SortMeta } from 'primeng/api' | 1 | import { SortMeta } from 'primeng/api' |
2 | import { Component, OnInit } from '@angular/core' | 2 | import { Component, OnInit } from '@angular/core' |
3 | import { Notifier, RestPagination, RestTable } from '@app/core' | 3 | import { Notifier, RestPagination, RestTable } from '@app/core' |
4 | import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage' | 4 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' |
5 | import { I18n } from '@ngx-translate/i18n-polyfill' | 5 | import { I18n } from '@ngx-translate/i18n-polyfill' |
6 | import { Job, JobState, JobType } from '@shared/models' | 6 | import { Job, JobState, JobType } from '@shared/models' |
7 | import { JobStateClient } from '../../../../types/job-state-client.type' | 7 | import { JobStateClient } from '../../../../types/job-state-client.type' |
diff --git a/client/src/app/+videos/+video-watch/video-watch-playlist.component.ts b/client/src/app/+videos/+video-watch/video-watch-playlist.component.ts index 2c21be643..519ce2974 100644 --- a/client/src/app/+videos/+video-watch/video-watch-playlist.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch-playlist.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Component, Input } from '@angular/core' | 1 | import { Component, Input } from '@angular/core' |
2 | import { Router } from '@angular/router' | 2 | import { Router } from '@angular/router' |
3 | import { AuthService, ComponentPagination, LocalStorageService, Notifier, SessionStorageService, UserService } from '@app/core' | 3 | import { AuthService, ComponentPagination, LocalStorageService, Notifier, SessionStorageService, UserService } from '@app/core' |
4 | import { peertubeLocalStorage, peertubeSessionStorage } from '@app/helpers/peertube-web-storage' | 4 | import { peertubeLocalStorage, peertubeSessionStorage } from '@root-helpers/peertube-web-storage' |
5 | import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist' | 5 | import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist' |
6 | import { I18n } from '@ngx-translate/i18n-polyfill' | 6 | import { I18n } from '@ngx-translate/i18n-polyfill' |
7 | import { VideoDetails, VideoPlaylistPrivacy } from '@shared/models' | 7 | import { VideoDetails, VideoPlaylistPrivacy } from '@shared/models' |
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts index e355faf25..bb0830d99 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts | |||
@@ -7,7 +7,8 @@ import { ActivatedRoute, Router } from '@angular/router' | |||
7 | import { AuthService, AuthUser, ConfirmService, MarkdownService, Notifier, RestExtractor, ServerService, UserService } from '@app/core' | 7 | import { AuthService, AuthUser, ConfirmService, MarkdownService, Notifier, RestExtractor, ServerService, UserService } from '@app/core' |
8 | import { HooksService } from '@app/core/plugins/hooks.service' | 8 | import { HooksService } from '@app/core/plugins/hooks.service' |
9 | import { RedirectService } from '@app/core/routing/redirect.service' | 9 | import { RedirectService } from '@app/core/routing/redirect.service' |
10 | import { isXPercentInViewport, peertubeLocalStorage, scrollToTop } from '@app/helpers' | 10 | import { isXPercentInViewport, scrollToTop } from '@app/helpers' |
11 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' | ||
11 | import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' | 12 | import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' |
12 | import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' | 13 | import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' |
13 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' | 14 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' |
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index a62aa4870..dee7fd056 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts | |||
@@ -15,7 +15,8 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | |||
15 | import { I18n } from '@ngx-translate/i18n-polyfill' | 15 | import { I18n } from '@ngx-translate/i18n-polyfill' |
16 | import { BroadcastMessageLevel, getShortLocale, is18nPath, ServerConfig, UserRole } from '@shared/models' | 16 | import { BroadcastMessageLevel, getShortLocale, is18nPath, ServerConfig, UserRole } from '@shared/models' |
17 | import { MenuService } from './core/menu/menu.service' | 17 | import { MenuService } from './core/menu/menu.service' |
18 | import { peertubeLocalStorage, POP_STATE_MODAL_DISMISS } from './helpers' | 18 | import { POP_STATE_MODAL_DISMISS } from './helpers' |
19 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' | ||
19 | import { InstanceService } from './shared/shared-instance' | 20 | import { InstanceService } from './shared/shared-instance' |
20 | 21 | ||
21 | @Component({ | 22 | @Component({ |
diff --git a/client/src/app/core/auth/auth-user.model.ts b/client/src/app/core/auth/auth-user.model.ts index 88b730938..ee61ff881 100644 --- a/client/src/app/core/auth/auth-user.model.ts +++ b/client/src/app/core/auth/auth-user.model.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Observable, of } from 'rxjs' | 1 | import { Observable, of } from 'rxjs' |
2 | import { map } from 'rxjs/operators' | 2 | import { map } from 'rxjs/operators' |
3 | import { User } from '@app/core/users/user.model' | 3 | import { User } from '@app/core/users/user.model' |
4 | import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage' | 4 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' |
5 | import { | 5 | import { |
6 | hasUserRight, | 6 | hasUserRight, |
7 | MyUser as ServerMyUserModel, | 7 | MyUser as ServerMyUserModel, |
@@ -12,66 +12,7 @@ import { | |||
12 | UserRole, | 12 | UserRole, |
13 | UserVideoQuota | 13 | UserVideoQuota |
14 | } from '@shared/models' | 14 | } from '@shared/models' |
15 | 15 | import { TokenOptions, Tokens } from '../../../root-helpers/pure-auth-user.model' | |
16 | export type TokenOptions = { | ||
17 | accessToken: string | ||
18 | refreshToken: string | ||
19 | tokenType: string | ||
20 | } | ||
21 | |||
22 | // Private class only used by User | ||
23 | class Tokens { | ||
24 | private static KEYS = { | ||
25 | ACCESS_TOKEN: 'access_token', | ||
26 | REFRESH_TOKEN: 'refresh_token', | ||
27 | TOKEN_TYPE: 'token_type' | ||
28 | } | ||
29 | |||
30 | accessToken: string | ||
31 | refreshToken: string | ||
32 | tokenType: string | ||
33 | |||
34 | static load () { | ||
35 | const accessTokenLocalStorage = peertubeLocalStorage.getItem(this.KEYS.ACCESS_TOKEN) | ||
36 | const refreshTokenLocalStorage = peertubeLocalStorage.getItem(this.KEYS.REFRESH_TOKEN) | ||
37 | const tokenTypeLocalStorage = peertubeLocalStorage.getItem(this.KEYS.TOKEN_TYPE) | ||
38 | |||
39 | if (accessTokenLocalStorage && refreshTokenLocalStorage && tokenTypeLocalStorage) { | ||
40 | return new Tokens({ | ||
41 | accessToken: accessTokenLocalStorage, | ||
42 | refreshToken: refreshTokenLocalStorage, | ||
43 | tokenType: tokenTypeLocalStorage | ||
44 | }) | ||
45 | } | ||
46 | |||
47 | return null | ||
48 | } | ||
49 | |||
50 | static flush () { | ||
51 | peertubeLocalStorage.removeItem(this.KEYS.ACCESS_TOKEN) | ||
52 | peertubeLocalStorage.removeItem(this.KEYS.REFRESH_TOKEN) | ||
53 | peertubeLocalStorage.removeItem(this.KEYS.TOKEN_TYPE) | ||
54 | } | ||
55 | |||
56 | constructor (hash?: TokenOptions) { | ||
57 | if (hash) { | ||
58 | this.accessToken = hash.accessToken | ||
59 | this.refreshToken = hash.refreshToken | ||
60 | |||
61 | if (hash.tokenType === 'bearer') { | ||
62 | this.tokenType = 'Bearer' | ||
63 | } else { | ||
64 | this.tokenType = hash.tokenType | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | |||
69 | save () { | ||
70 | peertubeLocalStorage.setItem(Tokens.KEYS.ACCESS_TOKEN, this.accessToken) | ||
71 | peertubeLocalStorage.setItem(Tokens.KEYS.REFRESH_TOKEN, this.refreshToken) | ||
72 | peertubeLocalStorage.setItem(Tokens.KEYS.TOKEN_TYPE, this.tokenType) | ||
73 | } | ||
74 | } | ||
75 | 16 | ||
76 | export class AuthUser extends User implements ServerMyUserModel { | 17 | export class AuthUser extends User implements ServerMyUserModel { |
77 | tokens: Tokens | 18 | tokens: Tokens |
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index 94262b9aa..d3dc48267 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts | |||
@@ -5,7 +5,7 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http' | |||
5 | import { Injectable } from '@angular/core' | 5 | import { Injectable } from '@angular/core' |
6 | import { Router } from '@angular/router' | 6 | import { Router } from '@angular/router' |
7 | import { Notifier } from '@app/core/notification/notifier.service' | 7 | import { Notifier } from '@app/core/notification/notifier.service' |
8 | import { objectToUrlEncoded, peertubeLocalStorage } from '@app/helpers' | 8 | import { objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index' |
9 | import { I18n } from '@ngx-translate/i18n-polyfill' | 9 | import { I18n } from '@ngx-translate/i18n-polyfill' |
10 | import { MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models' | 10 | import { MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models' |
11 | import { environment } from '../../../environments/environment' | 11 | import { environment } from '../../../environments/environment' |
diff --git a/client/src/app/core/rest/rest-table.ts b/client/src/app/core/rest/rest-table.ts index e6328eddc..7e7e6f4f7 100644 --- a/client/src/app/core/rest/rest-table.ts +++ b/client/src/app/core/rest/rest-table.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage' | 1 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' |
2 | import { LazyLoadEvent, SortMeta } from 'primeng/api' | 2 | import { LazyLoadEvent, SortMeta } from 'primeng/api' |
3 | import { RestPagination } from './rest-pagination' | 3 | import { RestPagination } from './rest-pagination' |
4 | import { Subject } from 'rxjs' | 4 | import { Subject } from 'rxjs' |
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts index 32a135203..c69e0919a 100644 --- a/client/src/app/core/server/server.service.ts +++ b/client/src/app/core/server/server.service.ts | |||
@@ -2,7 +2,8 @@ import { Observable, of, Subject } from 'rxjs' | |||
2 | import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators' | 2 | import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators' |
3 | import { HttpClient } from '@angular/common/http' | 3 | import { HttpClient } from '@angular/common/http' |
4 | import { Inject, Injectable, LOCALE_ID } from '@angular/core' | 4 | import { Inject, Injectable, LOCALE_ID } from '@angular/core' |
5 | import { getDevLocale, isOnDevLocale, peertubeLocalStorage, sortBy } from '@app/helpers' | 5 | import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers' |
6 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' | ||
6 | import { | 7 | import { |
7 | getCompleteLocale, | 8 | getCompleteLocale, |
8 | isDefaultLocale, | 9 | isDefaultLocale, |
diff --git a/client/src/app/core/users/user.model.ts b/client/src/app/core/users/user.model.ts index 4256f370b..a94b35c46 100644 --- a/client/src/app/core/users/user.model.ts +++ b/client/src/app/core/users/user.model.ts | |||
@@ -10,23 +10,10 @@ import { | |||
10 | UserRole, | 10 | UserRole, |
11 | VideoChannel | 11 | VideoChannel |
12 | } from '@shared/models' | 12 | } from '@shared/models' |
13 | import { UserKeys } from '@root-helpers/user-keys' | ||
13 | 14 | ||
14 | export class User implements UserServerModel { | 15 | export class User implements UserServerModel { |
15 | static KEYS = { | 16 | static KEYS = UserKeys |
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: 'theme', | ||
27 | LAST_ACTIVE_THEME: 'last_active_theme', | ||
28 | VIDEO_LANGUAGES: 'video_languages' | ||
29 | } | ||
30 | 17 | ||
31 | id: number | 18 | id: number |
32 | username: string | 19 | username: string |
diff --git a/client/src/app/core/wrappers/storage.service.ts b/client/src/app/core/wrappers/storage.service.ts index 9a60b9785..ad3aca6b8 100644 --- a/client/src/app/core/wrappers/storage.service.ts +++ b/client/src/app/core/wrappers/storage.service.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Observable, Subject } from 'rxjs' | 1 | import { Observable, Subject } from 'rxjs' |
2 | import { filter } from 'rxjs/operators' | 2 | import { filter } from 'rxjs/operators' |
3 | import { Injectable } from '@angular/core' | 3 | import { Injectable } from '@angular/core' |
4 | import { peertubeLocalStorage, peertubeSessionStorage } from '@app/helpers' | 4 | import { peertubeLocalStorage, peertubeSessionStorage } from '@root-helpers/peertube-web-storage' |
5 | 5 | ||
6 | abstract class StorageService { | 6 | abstract class StorageService { |
7 | protected instance: Storage | 7 | protected instance: Storage |
diff --git a/client/src/app/helpers/index.ts b/client/src/app/helpers/index.ts index 06806402e..cc61255ba 100644 --- a/client/src/app/helpers/index.ts +++ b/client/src/app/helpers/index.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | export * from './locales' | 1 | export * from './locales' |
2 | export * from './constants' | 2 | export * from './constants' |
3 | export * from './i18n-utils' | 3 | export * from './i18n-utils' |
4 | export * from './peertube-web-storage' | ||
5 | export * from './utils' | 4 | export * from './utils' |
6 | export * from './zone' | 5 | export * from './zone' |
diff --git a/client/src/app/helpers/utils.ts b/client/src/app/helpers/utils.ts index 8e9f72adb..825b6ca96 100644 --- a/client/src/app/helpers/utils.ts +++ b/client/src/app/helpers/utils.ts | |||
@@ -81,15 +81,6 @@ function immutableAssign <A, B> (target: A, source: B) { | |||
81 | return Object.assign({}, target, source) | 81 | return Object.assign({}, target, source) |
82 | } | 82 | } |
83 | 83 | ||
84 | function objectToUrlEncoded (obj: any) { | ||
85 | const str: string[] = [] | ||
86 | for (const key of Object.keys(obj)) { | ||
87 | str.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key])) | ||
88 | } | ||
89 | |||
90 | return str.join('&') | ||
91 | } | ||
92 | |||
93 | // Thanks: https://gist.github.com/ghinda/8442a57f22099bdb2e34 | 84 | // Thanks: https://gist.github.com/ghinda/8442a57f22099bdb2e34 |
94 | function objectToFormData (obj: any, form?: FormData, namespace?: string) { | 85 | function objectToFormData (obj: any, form?: FormData, namespace?: string) { |
95 | const fd = form || new FormData() | 86 | const fd = form || new FormData() |
@@ -207,7 +198,6 @@ export { | |||
207 | sortBy, | 198 | sortBy, |
208 | durationToString, | 199 | durationToString, |
209 | lineFeedToHtml, | 200 | lineFeedToHtml, |
210 | objectToUrlEncoded, | ||
211 | getParameterByName, | 201 | getParameterByName, |
212 | populateAsyncUserVideoChannels, | 202 | populateAsyncUserVideoChannels, |
213 | getAbsoluteAPIUrl, | 203 | getAbsoluteAPIUrl, |
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.scss b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.scss index 7ed7c9e87..5da624963 100644 --- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.scss +++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.scss | |||
@@ -1,98 +1,9 @@ | |||
1 | @import 'variables'; | ||
2 | @import 'mixins'; | ||
3 | @import 'miniature'; | ||
4 | |||
5 | .table-video-link { | ||
6 | @include disable-outline; | ||
7 | |||
8 | position: relative; | ||
9 | top: 3px; | ||
10 | } | ||
11 | |||
12 | .table-comment-link, | ||
13 | .table-account-link { | ||
14 | @include disable-outline; | ||
15 | |||
16 | color: var(--mainForegroundColor); | ||
17 | |||
18 | ::ng-deep p:last-child { | ||
19 | margin: 0; | ||
20 | } | ||
21 | } | ||
22 | |||
23 | .table-account-link { | ||
24 | display: flex; | ||
25 | flex-direction: column; | ||
26 | } | ||
27 | |||
28 | .comment-flagged-account, | 1 | .comment-flagged-account, |
29 | .account-flagged-handle { | 2 | .account-flagged-handle { |
30 | font-size: 11px; | 3 | font-size: 11px; |
31 | color: var(--greyForegroundColor); | 4 | color: var(--greyForegroundColor); |
32 | } | 5 | } |
33 | 6 | ||
34 | .table-video { | ||
35 | display: inline-flex; | ||
36 | |||
37 | .table-video-image { | ||
38 | @include miniature-thumbnail; | ||
39 | |||
40 | $image-height: 45px; | ||
41 | |||
42 | height: $image-height; | ||
43 | width: #{(16/9) * $image-height}; | ||
44 | margin-right: 0.5rem; | ||
45 | border-radius: 2px; | ||
46 | border: none; | ||
47 | background: transparent; | ||
48 | display: inline-flex; | ||
49 | justify-content: center; | ||
50 | align-items: center; | ||
51 | position: relative; | ||
52 | |||
53 | img { | ||
54 | height: 100%; | ||
55 | width: 100%; | ||
56 | border-radius: 2px; | ||
57 | } | ||
58 | |||
59 | span { | ||
60 | color: pvar(--inputPlaceholderColor); | ||
61 | } | ||
62 | |||
63 | .table-video-image-label { | ||
64 | @include static-thumbnail-overlay; | ||
65 | position: absolute; | ||
66 | border-radius: 3px; | ||
67 | font-size: 10px; | ||
68 | padding: 0 3px; | ||
69 | line-height: 1.3; | ||
70 | bottom: 2px; | ||
71 | right: 2px; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | .table-video-text { | ||
76 | display: inline-flex; | ||
77 | flex-direction: column; | ||
78 | justify-content: center; | ||
79 | font-size: 90%; | ||
80 | color: pvar(--mainForegroundColor); | ||
81 | line-height: 1rem; | ||
82 | |||
83 | div .glyphicon { | ||
84 | font-size: 80%; | ||
85 | color: gray; | ||
86 | margin-left: 0.1rem; | ||
87 | } | ||
88 | |||
89 | div + div { | ||
90 | color: var(--greyForegroundColor); | ||
91 | font-size: 11px; | ||
92 | } | ||
93 | } | ||
94 | } | ||
95 | |||
96 | .abuse-states .glyphicon-comment { | 7 | .abuse-states .glyphicon-comment { |
97 | margin-left: 0.5rem; | 8 | margin-left: 0.5rem; |
98 | } | 9 | } |
diff --git a/client/src/app/shared/shared-moderation/moderation.scss b/client/src/app/shared/shared-moderation/moderation.scss index c9f0d0c42..c57872bae 100644 --- a/client/src/app/shared/shared-moderation/moderation.scss +++ b/client/src/app/shared/shared-moderation/moderation.scss | |||
@@ -65,3 +65,88 @@ my-action-dropdown.show { | |||
65 | display: block !important; | 65 | display: block !important; |
66 | } | 66 | } |
67 | } | 67 | } |
68 | |||
69 | .table-video-link { | ||
70 | @include disable-outline; | ||
71 | |||
72 | position: relative; | ||
73 | top: 3px; | ||
74 | } | ||
75 | |||
76 | .table-comment-link, | ||
77 | .table-account-link { | ||
78 | @include disable-outline; | ||
79 | |||
80 | color: var(--mainForegroundColor); | ||
81 | |||
82 | ::ng-deep p:last-child { | ||
83 | margin: 0; | ||
84 | } | ||
85 | } | ||
86 | |||
87 | .table-account-link { | ||
88 | display: flex; | ||
89 | flex-direction: column; | ||
90 | } | ||
91 | |||
92 | .table-video { | ||
93 | display: inline-flex; | ||
94 | |||
95 | .table-video-image { | ||
96 | @include miniature-thumbnail; | ||
97 | |||
98 | $image-height: 45px; | ||
99 | |||
100 | height: $image-height; | ||
101 | width: #{(16/9) * $image-height}; | ||
102 | margin-right: 0.5rem; | ||
103 | border-radius: 2px; | ||
104 | border: none; | ||
105 | background: transparent; | ||
106 | display: inline-flex; | ||
107 | justify-content: center; | ||
108 | align-items: center; | ||
109 | position: relative; | ||
110 | |||
111 | img { | ||
112 | height: 100%; | ||
113 | width: 100%; | ||
114 | border-radius: 2px; | ||
115 | } | ||
116 | |||
117 | span { | ||
118 | color: pvar(--inputPlaceholderColor); | ||
119 | } | ||
120 | |||
121 | .table-video-image-label { | ||
122 | @include static-thumbnail-overlay; | ||
123 | position: absolute; | ||
124 | border-radius: 3px; | ||
125 | font-size: 10px; | ||
126 | padding: 0 3px; | ||
127 | line-height: 1.3; | ||
128 | bottom: 2px; | ||
129 | right: 2px; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | .table-video-text { | ||
134 | display: inline-flex; | ||
135 | flex-direction: column; | ||
136 | justify-content: center; | ||
137 | font-size: 90%; | ||
138 | color: pvar(--mainForegroundColor); | ||
139 | line-height: 1rem; | ||
140 | |||
141 | div .glyphicon { | ||
142 | font-size: 80%; | ||
143 | color: gray; | ||
144 | margin-left: 0.1rem; | ||
145 | } | ||
146 | |||
147 | div + div { | ||
148 | color: var(--greyForegroundColor); | ||
149 | font-size: 11px; | ||
150 | } | ||
151 | } | ||
152 | } | ||
diff --git a/client/src/app/shared/shared-search/search.service.ts b/client/src/app/shared/shared-search/search.service.ts index 96b954c99..15c4a7012 100644 --- a/client/src/app/shared/shared-search/search.service.ts +++ b/client/src/app/shared/shared-search/search.service.ts | |||
@@ -3,7 +3,7 @@ import { catchError, map, switchMap } from 'rxjs/operators' | |||
3 | import { HttpClient, HttpParams } from '@angular/common/http' | 3 | import { HttpClient, HttpParams } from '@angular/common/http' |
4 | import { Injectable } from '@angular/core' | 4 | import { Injectable } from '@angular/core' |
5 | import { ComponentPaginationLight, RestExtractor, RestPagination, RestService } from '@app/core' | 5 | import { ComponentPaginationLight, RestExtractor, RestPagination, RestService } from '@app/core' |
6 | import { peertubeLocalStorage } from '@app/helpers' | 6 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' |
7 | import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' | 7 | import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' |
8 | import { ResultList, SearchTargetType, Video as VideoServerModel, VideoChannel as VideoChannelServerModel } from '@shared/models' | 8 | import { ResultList, SearchTargetType, Video as VideoServerModel, VideoChannel as VideoChannelServerModel } from '@shared/models' |
9 | import { environment } from '../../../environments/environment' | 9 | import { environment } from '../../../environments/environment' |
diff --git a/client/src/root-helpers/index.ts b/client/src/root-helpers/index.ts new file mode 100644 index 000000000..5ed4933f1 --- /dev/null +++ b/client/src/root-helpers/index.ts | |||
@@ -0,0 +1,4 @@ | |||
1 | export * from './peertube-web-storage' | ||
2 | export * from './utils' | ||
3 | export * from './user-keys' | ||
4 | export * from './pure-auth-user.model' | ||
diff --git a/client/src/app/helpers/peertube-web-storage.ts b/client/src/root-helpers/peertube-web-storage.ts index 0db1301bd..0db1301bd 100644 --- a/client/src/app/helpers/peertube-web-storage.ts +++ b/client/src/root-helpers/peertube-web-storage.ts | |||
diff --git a/client/src/root-helpers/pure-auth-user.model.ts b/client/src/root-helpers/pure-auth-user.model.ts new file mode 100644 index 000000000..81226da01 --- /dev/null +++ b/client/src/root-helpers/pure-auth-user.model.ts | |||
@@ -0,0 +1,123 @@ | |||
1 | // pure version of auth-user, that doesn't import app packages | ||
2 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' | ||
3 | import { | ||
4 | MyUser as ServerMyUserModel, | ||
5 | MyUserSpecialPlaylist, | ||
6 | NSFWPolicyType, | ||
7 | UserRole | ||
8 | } from '@shared/models' | ||
9 | import { UserKeys } from '@root-helpers/user-keys' | ||
10 | |||
11 | export type TokenOptions = { | ||
12 | accessToken: string | ||
13 | refreshToken: string | ||
14 | tokenType: string | ||
15 | } | ||
16 | |||
17 | // Private class only used by User | ||
18 | export class Tokens { | ||
19 | private static KEYS = { | ||
20 | ACCESS_TOKEN: 'access_token', | ||
21 | REFRESH_TOKEN: 'refresh_token', | ||
22 | TOKEN_TYPE: 'token_type' | ||
23 | } | ||
24 | |||
25 | accessToken: string | ||
26 | refreshToken: string | ||
27 | tokenType: string | ||
28 | |||
29 | static load () { | ||
30 | const accessTokenLocalStorage = peertubeLocalStorage.getItem(this.KEYS.ACCESS_TOKEN) | ||
31 | const refreshTokenLocalStorage = peertubeLocalStorage.getItem(this.KEYS.REFRESH_TOKEN) | ||
32 | const tokenTypeLocalStorage = peertubeLocalStorage.getItem(this.KEYS.TOKEN_TYPE) | ||
33 | |||
34 | if (accessTokenLocalStorage && refreshTokenLocalStorage && tokenTypeLocalStorage) { | ||
35 | return new Tokens({ | ||
36 | accessToken: accessTokenLocalStorage, | ||
37 | refreshToken: refreshTokenLocalStorage, | ||
38 | tokenType: tokenTypeLocalStorage | ||
39 | }) | ||
40 | } | ||
41 | |||
42 | return null | ||
43 | } | ||
44 | |||
45 | static flush () { | ||
46 | peertubeLocalStorage.removeItem(this.KEYS.ACCESS_TOKEN) | ||
47 | peertubeLocalStorage.removeItem(this.KEYS.REFRESH_TOKEN) | ||
48 | peertubeLocalStorage.removeItem(this.KEYS.TOKEN_TYPE) | ||
49 | } | ||
50 | |||
51 | constructor (hash?: TokenOptions) { | ||
52 | if (hash) { | ||
53 | this.accessToken = hash.accessToken | ||
54 | this.refreshToken = hash.refreshToken | ||
55 | |||
56 | if (hash.tokenType === 'bearer') { | ||
57 | this.tokenType = 'Bearer' | ||
58 | } else { | ||
59 | this.tokenType = hash.tokenType | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | |||
64 | save () { | ||
65 | peertubeLocalStorage.setItem(Tokens.KEYS.ACCESS_TOKEN, this.accessToken) | ||
66 | peertubeLocalStorage.setItem(Tokens.KEYS.REFRESH_TOKEN, this.refreshToken) | ||
67 | peertubeLocalStorage.setItem(Tokens.KEYS.TOKEN_TYPE, this.tokenType) | ||
68 | } | ||
69 | } | ||
70 | |||
71 | export class PureAuthUser { | ||
72 | tokens: Tokens | ||
73 | specialPlaylists: MyUserSpecialPlaylist[] | ||
74 | |||
75 | canSeeVideosLink = true | ||
76 | |||
77 | static load () { | ||
78 | const usernameLocalStorage = peertubeLocalStorage.getItem(UserKeys.USERNAME) | ||
79 | if (usernameLocalStorage) { | ||
80 | return new PureAuthUser( | ||
81 | { | ||
82 | id: parseInt(peertubeLocalStorage.getItem(UserKeys.ID), 10), | ||
83 | username: peertubeLocalStorage.getItem(UserKeys.USERNAME), | ||
84 | email: peertubeLocalStorage.getItem(UserKeys.EMAIL), | ||
85 | role: parseInt(peertubeLocalStorage.getItem(UserKeys.ROLE), 10) as UserRole, | ||
86 | nsfwPolicy: peertubeLocalStorage.getItem(UserKeys.NSFW_POLICY) as NSFWPolicyType, | ||
87 | webTorrentEnabled: peertubeLocalStorage.getItem(UserKeys.WEBTORRENT_ENABLED) === 'true', | ||
88 | autoPlayVideo: peertubeLocalStorage.getItem(UserKeys.AUTO_PLAY_VIDEO) === 'true', | ||
89 | videosHistoryEnabled: peertubeLocalStorage.getItem(UserKeys.VIDEOS_HISTORY_ENABLED) === 'true' | ||
90 | }, | ||
91 | Tokens.load() | ||
92 | ) | ||
93 | } | ||
94 | |||
95 | return null | ||
96 | } | ||
97 | |||
98 | constructor (userHash: Partial<ServerMyUserModel>, hashTokens: TokenOptions) { | ||
99 | this.tokens = new Tokens(hashTokens) | ||
100 | this.specialPlaylists = userHash.specialPlaylists | ||
101 | } | ||
102 | |||
103 | getAccessToken () { | ||
104 | return this.tokens.accessToken | ||
105 | } | ||
106 | |||
107 | getRefreshToken () { | ||
108 | return this.tokens.refreshToken | ||
109 | } | ||
110 | |||
111 | getTokenType () { | ||
112 | return this.tokens.tokenType | ||
113 | } | ||
114 | |||
115 | refreshTokens (accessToken: string, refreshToken: string) { | ||
116 | this.tokens.accessToken = accessToken | ||
117 | this.tokens.refreshToken = refreshToken | ||
118 | } | ||
119 | |||
120 | save () { | ||
121 | this.tokens.save() | ||
122 | } | ||
123 | } | ||
diff --git a/client/src/root-helpers/user-keys.ts b/client/src/root-helpers/user-keys.ts new file mode 100644 index 000000000..897be8c43 --- /dev/null +++ b/client/src/root-helpers/user-keys.ts | |||
@@ -0,0 +1,15 @@ | |||
1 | export const UserKeys = { | ||
2 | ID: 'id', | ||
3 | ROLE: 'role', | ||
4 | EMAIL: 'email', | ||
5 | VIDEOS_HISTORY_ENABLED: 'videos-history-enabled', | ||
6 | USERNAME: 'username', | ||
7 | NSFW_POLICY: 'nsfw_policy', | ||
8 | WEBTORRENT_ENABLED: 'peertube-videojs-' + 'webtorrent_enabled', | ||
9 | AUTO_PLAY_VIDEO: 'auto_play_video', | ||
10 | SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO: 'auto_play_next_video', | ||
11 | AUTO_PLAY_VIDEO_PLAYLIST: 'auto_play_video_playlist', | ||
12 | THEME: 'theme', | ||
13 | LAST_ACTIVE_THEME: 'last_active_theme', | ||
14 | VIDEO_LANGUAGES: 'video_languages' | ||
15 | } | ||
diff --git a/client/src/root-helpers/utils.ts b/client/src/root-helpers/utils.ts new file mode 100644 index 000000000..acfb565a3 --- /dev/null +++ b/client/src/root-helpers/utils.ts | |||
@@ -0,0 +1,12 @@ | |||
1 | function objectToUrlEncoded (obj: any) { | ||
2 | const str: string[] = [] | ||
3 | for (const key of Object.keys(obj)) { | ||
4 | str.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key])) | ||
5 | } | ||
6 | |||
7 | return str.join('&') | ||
8 | } | ||
9 | |||
10 | export { | ||
11 | objectToUrlEncoded | ||
12 | } | ||
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index dca23ca7e..5bf38a04c 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts | |||
@@ -4,7 +4,8 @@ import { | |||
4 | peertubeTranslate, | 4 | peertubeTranslate, |
5 | ResultList, | 5 | ResultList, |
6 | ServerConfig, | 6 | ServerConfig, |
7 | VideoDetails | 7 | VideoDetails, |
8 | UserRefreshToken | ||
8 | } from '../../../../shared' | 9 | } from '../../../../shared' |
9 | import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' | 10 | import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' |
10 | import { | 11 | import { |
@@ -17,7 +18,7 @@ import { PeerTubeEmbedApi } from './embed-api' | |||
17 | import { TranslationsManager } from '../../assets/player/translations-manager' | 18 | import { TranslationsManager } from '../../assets/player/translations-manager' |
18 | import videojs from 'video.js' | 19 | import videojs from 'video.js' |
19 | import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' | 20 | import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' |
20 | import { AuthUser } from '@app/core/auth/auth-user.model' | 21 | import { PureAuthUser, objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index' |
21 | 22 | ||
22 | type Translations = { [ id: string ]: string } | 23 | type Translations = { [ id: string ]: string } |
23 | 24 | ||
@@ -43,8 +44,12 @@ export class PeerTubeEmbed { | |||
43 | mode: PlayerMode | 44 | mode: PlayerMode |
44 | scope = 'peertube' | 45 | scope = 'peertube' |
45 | 46 | ||
46 | user: AuthUser | 47 | user: PureAuthUser |
47 | headers = new Headers() | 48 | headers = new Headers() |
49 | LOCAL_STORAGE_OAUTH_CLIENT_KEYS = { | ||
50 | CLIENT_ID: 'client_id', | ||
51 | CLIENT_SECRET: 'client_secret' | ||
52 | } | ||
48 | 53 | ||
49 | static async main () { | 54 | static async main () { |
50 | const videoContainerId = 'video-container' | 55 | const videoContainerId = 'video-container' |
@@ -60,12 +65,62 @@ export class PeerTubeEmbed { | |||
60 | return window.location.origin + '/api/v1/videos/' + id | 65 | return window.location.origin + '/api/v1/videos/' + id |
61 | } | 66 | } |
62 | 67 | ||
68 | refreshFetch (url: string, options?: Object) { | ||
69 | return fetch(url, options) | ||
70 | .then((res: Response) => { | ||
71 | if (res.status !== 401) return res | ||
72 | |||
73 | // 401 unauthorized is not catch-ed, but then-ed | ||
74 | const error = res | ||
75 | |||
76 | const refreshingTokenPromise = new Promise((resolve, reject) => { | ||
77 | const clientId: string = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID) | ||
78 | const clientSecret: string = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_SECRET) | ||
79 | const headers = new Headers() | ||
80 | headers.set('Content-Type', 'application/x-www-form-urlencoded') | ||
81 | const data = { | ||
82 | refresh_token: this.user.getRefreshToken(), | ||
83 | client_id: clientId, | ||
84 | client_secret: clientSecret, | ||
85 | response_type: 'code', | ||
86 | grant_type: 'refresh_token' | ||
87 | } | ||
88 | |||
89 | fetch('/api/v1/users/token', { | ||
90 | headers, | ||
91 | method: 'POST', | ||
92 | body: objectToUrlEncoded(data) | ||
93 | }) | ||
94 | .then(res => res.json()) | ||
95 | .then((obj: UserRefreshToken) => { | ||
96 | this.user.refreshTokens(obj.access_token, obj.refresh_token) | ||
97 | this.user.save() | ||
98 | this.headers.set('Authorization', `${this.user.getTokenType()} ${this.user.getAccessToken()}`) | ||
99 | resolve() | ||
100 | }) | ||
101 | .catch((refreshTokenError: any) => { | ||
102 | reject(refreshTokenError) | ||
103 | }) | ||
104 | }) | ||
105 | |||
106 | return refreshingTokenPromise | ||
107 | .catch(() => { | ||
108 | // If refreshing fails, continue with original error | ||
109 | throw error | ||
110 | }) | ||
111 | .then(() => fetch(url, { | ||
112 | ...options, | ||
113 | headers: this.headers | ||
114 | })) | ||
115 | }) | ||
116 | } | ||
117 | |||
63 | loadVideoInfo (videoId: string): Promise<Response> { | 118 | loadVideoInfo (videoId: string): Promise<Response> { |
64 | return fetch(this.getVideoUrl(videoId), { headers: this.headers }) | 119 | return this.refreshFetch(this.getVideoUrl(videoId), { headers: this.headers }) |
65 | } | 120 | } |
66 | 121 | ||
67 | loadVideoCaptions (videoId: string): Promise<Response> { | 122 | loadVideoCaptions (videoId: string): Promise<Response> { |
68 | return fetch(this.getVideoUrl(videoId) + '/captions', { headers: this.headers }) | 123 | return fetch(this.getVideoUrl(videoId) + '/captions') |
69 | } | 124 | } |
70 | 125 | ||
71 | loadConfig (): Promise<Response> { | 126 | loadConfig (): Promise<Response> { |
@@ -115,7 +170,7 @@ export class PeerTubeEmbed { | |||
115 | 170 | ||
116 | async init () { | 171 | async init () { |
117 | try { | 172 | try { |
118 | this.user = AuthUser.load() | 173 | this.user = PureAuthUser.load() |
119 | await this.initCore() | 174 | await this.initCore() |
120 | } catch (e) { | 175 | } catch (e) { |
121 | console.error(e) | 176 | console.error(e) |
diff --git a/client/tsconfig.json b/client/tsconfig.json index 752f5b35f..a7b690c7b 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json | |||
@@ -28,6 +28,7 @@ | |||
28 | "video.js": [ "node_modules/video.js/core" ], | 28 | "video.js": [ "node_modules/video.js/core" ], |
29 | "@app/*": [ "src/app/*" ], | 29 | "@app/*": [ "src/app/*" ], |
30 | "@shared/*": [ "../shared/*" ], | 30 | "@shared/*": [ "../shared/*" ], |
31 | "@root-helpers/*": [ "src/root-helpers/*" ], | ||
31 | "fs": [ "src/shims/noop.ts" ], | 32 | "fs": [ "src/shims/noop.ts" ], |
32 | "http": [ "src/shims/http.ts" ], | 33 | "http": [ "src/shims/http.ts" ], |
33 | "https": [ "src/shims/https.ts" ], | 34 | "https": [ "src/shims/https.ts" ], |
diff --git a/client/webpack/webpack.video-embed.js b/client/webpack/webpack.video-embed.js index 319b00e5d..b6a1c4c05 100644 --- a/client/webpack/webpack.video-embed.js +++ b/client/webpack/webpack.video-embed.js | |||
@@ -28,7 +28,7 @@ module.exports = function () { | |||
28 | 28 | ||
29 | alias: { | 29 | alias: { |
30 | 'video.js$': path.resolve('node_modules/video.js/core.js'), | 30 | 'video.js$': path.resolve('node_modules/video.js/core.js'), |
31 | '@app': path.resolve('src/app'), | 31 | '@root-helpers': path.resolve('src/root-helpers'), |
32 | '@shared': path.resolve('../shared') | 32 | '@shared': path.resolve('../shared') |
33 | } | 33 | } |
34 | }, | 34 | }, |