aboutsummaryrefslogtreecommitdiffhomepage
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts2
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts2
-rw-r--r--client/src/app/+admin/system/jobs/jobs.component.ts2
-rw-r--r--client/src/app/+videos/+video-watch/video-watch-playlist.component.ts2
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts3
-rw-r--r--client/src/app/app.component.ts3
-rw-r--r--client/src/app/core/auth/auth-user.model.ts63
-rw-r--r--client/src/app/core/auth/auth.service.ts2
-rw-r--r--client/src/app/core/rest/rest-table.ts2
-rw-r--r--client/src/app/core/server/server.service.ts3
-rw-r--r--client/src/app/core/users/user.model.ts17
-rw-r--r--client/src/app/core/wrappers/storage.service.ts2
-rw-r--r--client/src/app/helpers/index.ts1
-rw-r--r--client/src/app/helpers/utils.ts10
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.scss89
-rw-r--r--client/src/app/shared/shared-moderation/moderation.scss85
-rw-r--r--client/src/app/shared/shared-search/search.service.ts2
-rw-r--r--client/src/root-helpers/index.ts4
-rw-r--r--client/src/root-helpers/peertube-web-storage.ts (renamed from client/src/app/helpers/peertube-web-storage.ts)0
-rw-r--r--client/src/root-helpers/pure-auth-user.model.ts123
-rw-r--r--client/src/root-helpers/user-keys.ts15
-rw-r--r--client/src/root-helpers/utils.ts12
-rw-r--r--client/src/standalone/videos/embed.ts67
-rw-r--r--client/tsconfig.json1
-rw-r--r--client/webpack/webpack.video-embed.js2
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'
2import { SortMeta } from 'primeng/api' 2import { SortMeta } from 'primeng/api'
3import { Component, OnInit } from '@angular/core' 3import { Component, OnInit } from '@angular/core'
4import { ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' 4import { ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
5import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage' 5import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
6import { RedundancyService } from '@app/shared/shared-main' 6import { RedundancyService } from '@app/shared/shared-main'
7import { I18n } from '@ngx-translate/i18n-polyfill' 7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models' 8import { 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})
19export class VideoBlockListComponent extends RestTable implements OnInit, AfterViewInit { 19export 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 @@
1import { SortMeta } from 'primeng/api' 1import { SortMeta } from 'primeng/api'
2import { Component, OnInit } from '@angular/core' 2import { Component, OnInit } from '@angular/core'
3import { Notifier, RestPagination, RestTable } from '@app/core' 3import { Notifier, RestPagination, RestTable } from '@app/core'
4import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage' 4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
5import { I18n } from '@ngx-translate/i18n-polyfill' 5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { Job, JobState, JobType } from '@shared/models' 6import { Job, JobState, JobType } from '@shared/models'
7import { JobStateClient } from '../../../../types/job-state-client.type' 7import { 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 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { Router } from '@angular/router' 2import { Router } from '@angular/router'
3import { AuthService, ComponentPagination, LocalStorageService, Notifier, SessionStorageService, UserService } from '@app/core' 3import { AuthService, ComponentPagination, LocalStorageService, Notifier, SessionStorageService, UserService } from '@app/core'
4import { peertubeLocalStorage, peertubeSessionStorage } from '@app/helpers/peertube-web-storage' 4import { peertubeLocalStorage, peertubeSessionStorage } from '@root-helpers/peertube-web-storage'
5import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist' 5import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist'
6import { I18n } from '@ngx-translate/i18n-polyfill' 6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { VideoDetails, VideoPlaylistPrivacy } from '@shared/models' 7import { 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'
7import { AuthService, AuthUser, ConfirmService, MarkdownService, Notifier, RestExtractor, ServerService, UserService } from '@app/core' 7import { AuthService, AuthUser, ConfirmService, MarkdownService, Notifier, RestExtractor, ServerService, UserService } from '@app/core'
8import { HooksService } from '@app/core/plugins/hooks.service' 8import { HooksService } from '@app/core/plugins/hooks.service'
9import { RedirectService } from '@app/core/routing/redirect.service' 9import { RedirectService } from '@app/core/routing/redirect.service'
10import { isXPercentInViewport, peertubeLocalStorage, scrollToTop } from '@app/helpers' 10import { isXPercentInViewport, scrollToTop } from '@app/helpers'
11import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
11import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' 12import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
12import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' 13import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
13import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 14import { 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'
15import { I18n } from '@ngx-translate/i18n-polyfill' 15import { I18n } from '@ngx-translate/i18n-polyfill'
16import { BroadcastMessageLevel, getShortLocale, is18nPath, ServerConfig, UserRole } from '@shared/models' 16import { BroadcastMessageLevel, getShortLocale, is18nPath, ServerConfig, UserRole } from '@shared/models'
17import { MenuService } from './core/menu/menu.service' 17import { MenuService } from './core/menu/menu.service'
18import { peertubeLocalStorage, POP_STATE_MODAL_DISMISS } from './helpers' 18import { POP_STATE_MODAL_DISMISS } from './helpers'
19import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
19import { InstanceService } from './shared/shared-instance' 20import { 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 @@
1import { Observable, of } from 'rxjs' 1import { Observable, of } from 'rxjs'
2import { map } from 'rxjs/operators' 2import { map } from 'rxjs/operators'
3import { User } from '@app/core/users/user.model' 3import { User } from '@app/core/users/user.model'
4import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage' 4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
5import { 5import {
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 15import { TokenOptions, Tokens } from '../../../root-helpers/pure-auth-user.model'
16export type TokenOptions = {
17 accessToken: string
18 refreshToken: string
19 tokenType: string
20}
21
22// Private class only used by User
23class 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
76export class AuthUser extends User implements ServerMyUserModel { 17export 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'
5import { Injectable } from '@angular/core' 5import { Injectable } from '@angular/core'
6import { Router } from '@angular/router' 6import { Router } from '@angular/router'
7import { Notifier } from '@app/core/notification/notifier.service' 7import { Notifier } from '@app/core/notification/notifier.service'
8import { objectToUrlEncoded, peertubeLocalStorage } from '@app/helpers' 8import { objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index'
9import { I18n } from '@ngx-translate/i18n-polyfill' 9import { I18n } from '@ngx-translate/i18n-polyfill'
10import { MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models' 10import { MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models'
11import { environment } from '../../../environments/environment' 11import { 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 @@
1import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage' 1import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
2import { LazyLoadEvent, SortMeta } from 'primeng/api' 2import { LazyLoadEvent, SortMeta } from 'primeng/api'
3import { RestPagination } from './rest-pagination' 3import { RestPagination } from './rest-pagination'
4import { Subject } from 'rxjs' 4import { 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'
2import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators' 2import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators'
3import { HttpClient } from '@angular/common/http' 3import { HttpClient } from '@angular/common/http'
4import { Inject, Injectable, LOCALE_ID } from '@angular/core' 4import { Inject, Injectable, LOCALE_ID } from '@angular/core'
5import { getDevLocale, isOnDevLocale, peertubeLocalStorage, sortBy } from '@app/helpers' 5import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
6import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
6import { 7import {
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'
13import { UserKeys } from '@root-helpers/user-keys'
13 14
14export class User implements UserServerModel { 15export 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 @@
1import { Observable, Subject } from 'rxjs' 1import { Observable, Subject } from 'rxjs'
2import { filter } from 'rxjs/operators' 2import { filter } from 'rxjs/operators'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { peertubeLocalStorage, peertubeSessionStorage } from '@app/helpers' 4import { peertubeLocalStorage, peertubeSessionStorage } from '@root-helpers/peertube-web-storage'
5 5
6abstract class StorageService { 6abstract 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 @@
1export * from './locales' 1export * from './locales'
2export * from './constants' 2export * from './constants'
3export * from './i18n-utils' 3export * from './i18n-utils'
4export * from './peertube-web-storage'
5export * from './utils' 4export * from './utils'
6export * from './zone' 5export * 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
84function 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
94function objectToFormData (obj: any, form?: FormData, namespace?: string) { 85function 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'
3import { HttpClient, HttpParams } from '@angular/common/http' 3import { HttpClient, HttpParams } from '@angular/common/http'
4import { Injectable } from '@angular/core' 4import { Injectable } from '@angular/core'
5import { ComponentPaginationLight, RestExtractor, RestPagination, RestService } from '@app/core' 5import { ComponentPaginationLight, RestExtractor, RestPagination, RestService } from '@app/core'
6import { peertubeLocalStorage } from '@app/helpers' 6import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
7import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' 7import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
8import { ResultList, SearchTargetType, Video as VideoServerModel, VideoChannel as VideoChannelServerModel } from '@shared/models' 8import { ResultList, SearchTargetType, Video as VideoServerModel, VideoChannel as VideoChannelServerModel } from '@shared/models'
9import { environment } from '../../../environments/environment' 9import { 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 @@
1export * from './peertube-web-storage'
2export * from './utils'
3export * from './user-keys'
4export * 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
2import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
3import {
4 MyUser as ServerMyUserModel,
5 MyUserSpecialPlaylist,
6 NSFWPolicyType,
7 UserRole
8} from '@shared/models'
9import { UserKeys } from '@root-helpers/user-keys'
10
11export type TokenOptions = {
12 accessToken: string
13 refreshToken: string
14 tokenType: string
15}
16
17// Private class only used by User
18export 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
71export 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 @@
1export 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 @@
1function 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
10export {
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'
9import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' 10import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
10import { 11import {
@@ -17,7 +18,7 @@ import { PeerTubeEmbedApi } from './embed-api'
17import { TranslationsManager } from '../../assets/player/translations-manager' 18import { TranslationsManager } from '../../assets/player/translations-manager'
18import videojs from 'video.js' 19import videojs from 'video.js'
19import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' 20import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
20import { AuthUser } from '@app/core/auth/auth-user.model' 21import { PureAuthUser, objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index'
21 22
22type Translations = { [ id: string ]: string } 23type 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 },