diff options
Diffstat (limited to 'client')
181 files changed, 4034 insertions, 2793 deletions
diff --git a/client/src/app/+about/about-follows/about-follows.component.html b/client/src/app/+about/about-follows/about-follows.component.html index 2cf890acf..e9139b503 100644 --- a/client/src/app/+about/about-follows/about-follows.component.html +++ b/client/src/app/+about/about-follows/about-follows.component.html | |||
@@ -1,7 +1,7 @@ | |||
1 | <div class="row"> | 1 | <div class="row"> |
2 | <h1 class="sr-only" i18n>Follows</h1> | 2 | <h1 class="sr-only" i18n>Follows</h1> |
3 | <div class="col-xl-6 col-md-12"> | 3 | <div class="col-xl-6 col-md-12"> |
4 | <h2 i18n class="subtitle">Followers instances ({{ followersPagination.totalItems }})</h2> | 4 | <h2 i18n class="subtitle">Follower instances ({{ followersPagination.totalItems }})</h2> |
5 | 5 | ||
6 | <div i18n class="no-results" *ngIf="followersPagination.totalItems === 0">This instance does not have instances followers.</div> | 6 | <div i18n class="no-results" *ngIf="followersPagination.totalItems === 0">This instance does not have instances followers.</div> |
7 | 7 | ||
diff --git a/client/src/app/+about/about-instance/about-instance.component.html b/client/src/app/+about/about-instance/about-instance.component.html index d8794d602..1f372090e 100644 --- a/client/src/app/+about/about-instance/about-instance.component.html +++ b/client/src/app/+about/about-instance/about-instance.component.html | |||
@@ -83,7 +83,7 @@ | |||
83 | fragment="business-model" | 83 | fragment="business-model" |
84 | #anchorLink | 84 | #anchorLink |
85 | (click)="onClickCopyLink(anchorLink)"> | 85 | (click)="onClickCopyLink(anchorLink)"> |
86 | <h3 i18n class="section-title">How we will pay for this instance</h3> | 86 | <h3 i18n class="section-title">How we will pay for keeping our instance running</h3> |
87 | </a> | 87 | </a> |
88 | 88 | ||
89 | <div [innerHTML]="html.businessModel"></div> | 89 | <div [innerHTML]="html.businessModel"></div> |
diff --git a/client/src/app/+accounts/account-about/account-about.component.html b/client/src/app/+accounts/account-about/account-about.component.html deleted file mode 100644 index e9e0e4079..000000000 --- a/client/src/app/+accounts/account-about/account-about.component.html +++ /dev/null | |||
@@ -1,15 +0,0 @@ | |||
1 | <h1 class="sr-only" i18n>About</h1> | ||
2 | <div class="margin-content"> | ||
3 | <div *ngIf="account" class="row no-gutters"> | ||
4 | <div class="block col-md-6 col-sm-12 pr-2"> | ||
5 | <h2 i18n class="small-title">DESCRIPTION</h2> | ||
6 | <div class="content" [innerHtml]="getAccountDescription()"></div> | ||
7 | </div> | ||
8 | |||
9 | <div class="block col-md-6 col-sm-12"> | ||
10 | <h2 i18n class="small-title">STATS</h2> | ||
11 | |||
12 | <div i18n class="content">Joined {{ account.createdAt | date }}</div> | ||
13 | </div> | ||
14 | </div> | ||
15 | </div> | ||
diff --git a/client/src/app/+accounts/account-about/account-about.component.scss b/client/src/app/+accounts/account-about/account-about.component.scss deleted file mode 100644 index 5bcd4b561..000000000 --- a/client/src/app/+accounts/account-about/account-about.component.scss +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .block { | ||
5 | margin-bottom: 40px; | ||
6 | |||
7 | .small-title { | ||
8 | @include in-content-small-title; | ||
9 | |||
10 | margin-bottom: 20px; | ||
11 | } | ||
12 | } | ||
diff --git a/client/src/app/+accounts/account-about/account-about.component.ts b/client/src/app/+accounts/account-about/account-about.component.ts deleted file mode 100644 index 6cf846d72..000000000 --- a/client/src/app/+accounts/account-about/account-about.component.ts +++ /dev/null | |||
@@ -1,40 +0,0 @@ | |||
1 | import { Subscription } from 'rxjs' | ||
2 | import { Component, OnDestroy, OnInit } from '@angular/core' | ||
3 | import { MarkdownService } from '@app/core' | ||
4 | import { Account, AccountService } from '@app/shared/shared-main' | ||
5 | |||
6 | @Component({ | ||
7 | selector: 'my-account-about', | ||
8 | templateUrl: './account-about.component.html', | ||
9 | styleUrls: [ './account-about.component.scss' ] | ||
10 | }) | ||
11 | export class AccountAboutComponent implements OnInit, OnDestroy { | ||
12 | account: Account | ||
13 | descriptionHTML = '' | ||
14 | |||
15 | private accountSub: Subscription | ||
16 | |||
17 | constructor ( | ||
18 | private accountService: AccountService, | ||
19 | private markdownService: MarkdownService | ||
20 | ) { } | ||
21 | |||
22 | ngOnInit () { | ||
23 | // Parent get the account for us | ||
24 | this.accountSub = this.accountService.accountLoaded | ||
25 | .subscribe(async account => { | ||
26 | this.account = account | ||
27 | this.descriptionHTML = await this.markdownService.textMarkdownToHTML(this.account.description, true) | ||
28 | }) | ||
29 | } | ||
30 | |||
31 | ngOnDestroy () { | ||
32 | if (this.accountSub) this.accountSub.unsubscribe() | ||
33 | } | ||
34 | |||
35 | getAccountDescription () { | ||
36 | if (this.descriptionHTML) return this.descriptionHTML | ||
37 | |||
38 | return $localize`No description` | ||
39 | } | ||
40 | } | ||
diff --git a/client/src/app/+accounts/account-search/account-search.component.ts b/client/src/app/+accounts/account-search/account-search.component.ts index dda4bf0c7..f54ab846a 100644 --- a/client/src/app/+accounts/account-search/account-search.component.ts +++ b/client/src/app/+accounts/account-search/account-search.component.ts | |||
@@ -64,9 +64,14 @@ export class AccountSearchComponent extends AbstractVideoList implements OnInit, | |||
64 | } | 64 | } |
65 | 65 | ||
66 | updateSearch (value: string) { | 66 | updateSearch (value: string) { |
67 | if (value === '') this.router.navigate(['../videos'], { relativeTo: this.route }) | ||
68 | this.search = value | 67 | this.search = value |
69 | 68 | ||
69 | if (!this.search) { | ||
70 | this.router.navigate([ '../videos' ], { relativeTo: this.route }) | ||
71 | return | ||
72 | } | ||
73 | |||
74 | this.videos = [] | ||
70 | this.reloadVideos() | 75 | this.reloadVideos() |
71 | } | 76 | } |
72 | 77 | ||
diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html index 5dbb341d2..19a4b3c9c 100644 --- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html +++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html | |||
@@ -1,33 +1,50 @@ | |||
1 | <h1 class="sr-only" i18n>Video channels</h1> | 1 | <h1 class="sr-only" i18n>Video channels</h1> |
2 | |||
2 | <div class="margin-content"> | 3 | <div class="margin-content"> |
3 | 4 | ||
4 | <div class="no-results" i18n *ngIf="channelPagination.totalItems === 0">This account does not have channels.</div> | 5 | <div class="no-results" i18n *ngIf="channelPagination.totalItems === 0">This account does not have channels.</div> |
5 | 6 | ||
6 | <div class="channels" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onChannelDataSubject.asObservable()"> | 7 | <div class="channels" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onChannelDataSubject.asObservable()"> |
7 | <div class="section channel" *ngFor="let videoChannel of videoChannels"> | 8 | <div class="channel" *ngFor="let videoChannel of videoChannels"> |
8 | <div class="section-title"> | 9 | |
9 | <a [routerLink]="getVideoChannelLink(videoChannel)" i18n-title title="See this video channel"> | 10 | <div class="channel-avatar-row"> |
11 | <a class="avatar-link" [routerLink]="getVideoChannelLink(videoChannel)" i18n-title title="See this video channel"> | ||
10 | <img [src]="videoChannel.avatarUrl" alt="Avatar" /> | 12 | <img [src]="videoChannel.avatarUrl" alt="Avatar" /> |
13 | </a> | ||
11 | 14 | ||
12 | <h2 class="section-title">{{ videoChannel.displayName }}</h2> | 15 | <h2> |
16 | <a [routerLink]="getVideoChannelLink(videoChannel)" i18n-title title="See this video channel"> | ||
17 | {{ videoChannel.displayName }} | ||
18 | </a> | ||
19 | </h2> | ||
20 | |||
21 | <div class="actor-counters"> | ||
13 | <div class="followers" i18n>{videoChannel.followersCount, plural, =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</div> | 22 | <div class="followers" i18n>{videoChannel.followersCount, plural, =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</div> |
14 | </a> | ||
15 | 23 | ||
16 | <my-subscribe-button [videoChannels]="[videoChannel]"></my-subscribe-button> | 24 | <span class="videos-count" *ngIf="getTotalVideosOf(videoChannel) !== undefined" i18n> |
25 | {getTotalVideosOf(videoChannel), plural, =1 {1 videos} other {{{ getTotalVideosOf(videoChannel) }} videos}} | ||
26 | </span> | ||
27 | </div> | ||
28 | |||
29 | <div class="description-html" [innerHTML]="getChannelDescription(videoChannel)"></div> | ||
17 | </div> | 30 | </div> |
18 | 31 | ||
19 | <div *ngIf="getVideosOf(videoChannel)" class="videos"> | 32 | <my-subscribe-button [videoChannels]="[videoChannel]"></my-subscribe-button> |
20 | <div class="no-results my-5" i18n *ngIf="getVideosOf(videoChannel).length === 0">This channel doesn't have any videos.</div> | 33 | |
34 | <a i18n class="button-show-channel peertube-button-link orange-button-inverted" [routerLink]="getVideoChannelLink(videoChannel)">Show this channel</a> | ||
35 | |||
36 | <div class="videos"> | ||
37 | <div class="no-results" i18n *ngIf="getTotalVideosOf(videoChannel) === 0">This channel doesn't have any videos.</div> | ||
21 | 38 | ||
22 | <my-video-miniature | 39 | <my-video-miniature |
23 | *ngFor="let video of getVideosOf(videoChannel)" | 40 | *ngFor="let video of getVideosOf(videoChannel)" |
24 | [video]="video" [user]="userMiniature" [displayVideoActions]="true" | 41 | [video]="video" [user]="userMiniature" [displayVideoActions]="true" [displayOptions]="miniatureDisplayOptions" |
25 | ></my-video-miniature> | 42 | ></my-video-miniature> |
26 | </div> | ||
27 | 43 | ||
28 | <a *ngIf="getVideosOf(videoChannel).length !== 0" class="show-more" i18n [routerLink]="getVideoChannelLink(videoChannel)"> | 44 | <div *ngIf="getTotalVideosOf(videoChannel)" class="miniature-show-channel"> |
29 | SHOW THIS CHANNEL | 45 | <a i18n [routerLink]="getVideoChannelLink(videoChannel)">SHOW THIS CHANNEL ></a> |
30 | </a> | 46 | </div> |
47 | </div> | ||
31 | </div> | 48 | </div> |
32 | </div> | 49 | </div> |
33 | </div> | 50 | </div> |
diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss b/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss index 4957e91d7..7e88802f3 100644 --- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss +++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss | |||
@@ -3,37 +3,175 @@ | |||
3 | @import '_miniature'; | 3 | @import '_miniature'; |
4 | 4 | ||
5 | .margin-content { | 5 | .margin-content { |
6 | @include fluid-videos-miniature-layout; | 6 | @include grid-videos-miniature-margins; |
7 | } | 7 | } |
8 | 8 | ||
9 | .section { | 9 | .channel { |
10 | @include miniature-rows; | 10 | max-width: $max-channels-width; |
11 | background-color: pvar(--channelBackgroundColor); | ||
12 | padding: 30px; | ||
11 | 13 | ||
12 | padding-top: 0 !important; | 14 | margin: 30px 0; |
13 | 15 | ||
14 | .section-title { | 16 | display: grid; |
17 | grid-template-columns: 1fr auto; | ||
18 | grid-template-rows: auto auto; | ||
19 | column-gap: 15px; | ||
20 | } | ||
21 | |||
22 | .channel-avatar-row { | ||
23 | grid-column: 1; | ||
24 | grid-row: 1; | ||
25 | |||
26 | display: grid; | ||
27 | grid-template-columns: auto auto 1fr; | ||
28 | grid-template-rows: auto 1fr; | ||
29 | |||
30 | .avatar-link { | ||
31 | grid-column: 1; | ||
32 | grid-row: 1 / 3; | ||
33 | margin-right: 30px; | ||
34 | } | ||
35 | |||
36 | img { | ||
37 | @include channel-avatar(75px); | ||
38 | } | ||
39 | |||
40 | a { | ||
41 | color: pvar(--mainForegroundColor); | ||
42 | } | ||
43 | |||
44 | h2 { | ||
45 | grid-row: 1; | ||
46 | grid-column: 2; | ||
47 | font-size: 20px; | ||
48 | line-height: 1; | ||
49 | font-weight: $font-bold; | ||
50 | margin: 0; | ||
51 | } | ||
52 | |||
53 | .actor-counters { | ||
54 | grid-row: 1; | ||
55 | grid-column: 3; | ||
56 | color: pvar(--greyForegroundColor); | ||
57 | font-size: 16px; | ||
58 | display: flex; | ||
15 | align-items: center; | 59 | align-items: center; |
60 | margin-left: 15px; | ||
16 | } | 61 | } |
17 | 62 | ||
18 | .videos { | 63 | .actor-counters > *:not(:last-child)::after { |
19 | overflow: hidden; | 64 | content: '•'; |
65 | margin: 0 10px; | ||
66 | color: pvar(--mainColor); | ||
67 | } | ||
20 | 68 | ||
21 | .no-results { | 69 | .description-html { |
22 | height: 50px; | 70 | grid-column: 2 / 4; |
23 | } | 71 | grid-row: 2; |
72 | |||
73 | max-height: 80px; | ||
74 | font-size: 16px; | ||
75 | |||
76 | @include fade-text(30px, pvar(--channelBackgroundColor)); | ||
77 | } | ||
78 | } | ||
79 | |||
80 | my-subscribe-button { | ||
81 | grid-row: 1; | ||
82 | grid-column: 2; | ||
83 | } | ||
84 | |||
85 | .videos { | ||
86 | display: flex; | ||
87 | grid-column: 1 / 3; | ||
88 | grid-row: 2; | ||
89 | margin-top: 30px; | ||
90 | |||
91 | position: relative; | ||
92 | overflow: hidden; | ||
93 | |||
94 | my-video-miniature { | ||
95 | margin-right: 15px; | ||
96 | min-width: $video-thumbnail-medium-width; | ||
97 | max-width: $video-thumbnail-medium-width; | ||
98 | } | ||
99 | |||
100 | .no-results { | ||
101 | height: auto; | ||
24 | } | 102 | } |
103 | } | ||
25 | 104 | ||
26 | my-video-miniature ::ng-deep my-video-actions-dropdown > my-action-dropdown { | 105 | .miniature-show-channel { |
27 | // Fix our overflow | 106 | height: 100%; |
28 | position: absolute; | 107 | position: absolute; |
108 | right: 0; | ||
109 | background: linear-gradient(90deg, transparent 0, pvar(--channelBackgroundColor) 45px); | ||
110 | padding: ($video-thumbnail-medium-height / 2 - 10px) 15px 0 60px; | ||
111 | z-index: z(miniature) + 1; | ||
112 | |||
113 | a { | ||
114 | color: pvar(--mainColor); | ||
115 | font-size: 16px; | ||
116 | font-weight: $font-semibold; | ||
29 | } | 117 | } |
30 | } | 118 | } |
31 | 119 | ||
120 | .button-show-channel { | ||
121 | display: none; | ||
122 | } | ||
123 | |||
32 | @media screen and (max-width: $mobile-view) { | 124 | @media screen and (max-width: $mobile-view) { |
33 | .section { | 125 | .channel { |
34 | .section-title { | 126 | padding: 15px; |
35 | flex-direction: column; | 127 | } |
36 | align-items: normal; | 128 | |
129 | .channel-avatar-row { | ||
130 | grid-template-columns: auto auto auto 1fr; | ||
131 | |||
132 | .avatar-link { | ||
133 | grid-row: 1 / 4; | ||
134 | } | ||
135 | |||
136 | h2 { | ||
137 | font-size: 16px; | ||
37 | } | 138 | } |
139 | |||
140 | .actor-counters { | ||
141 | margin: 0; | ||
142 | font-size: 13px; | ||
143 | grid-row: 2; | ||
144 | grid-column: 2 / 4; | ||
145 | } | ||
146 | |||
147 | .description-html { | ||
148 | grid-row: 3; | ||
149 | font-size: 14px; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | .show-channel a { | ||
154 | @include peertube-button-link; | ||
155 | @include orange-button-inverted; | ||
156 | } | ||
157 | |||
158 | .videos { | ||
159 | display: none; | ||
160 | } | ||
161 | |||
162 | my-subscribe-button, | ||
163 | .button-show-channel { | ||
164 | grid-column: 1 / 4; | ||
165 | grid-row: 3; | ||
166 | margin-top: 15px; | ||
167 | } | ||
168 | |||
169 | my-subscribe-button { | ||
170 | justify-self: start; | ||
171 | } | ||
172 | |||
173 | .button-show-channel { | ||
174 | display: block; | ||
175 | justify-self: end; | ||
38 | } | 176 | } |
39 | } | 177 | } |
diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts b/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts index f2beb6689..0628c7a96 100644 --- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts +++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts | |||
@@ -1,9 +1,10 @@ | |||
1 | import { from, Subject, Subscription } from 'rxjs' | 1 | import { from, Subject, Subscription } from 'rxjs' |
2 | import { concatMap, map, switchMap, tap } from 'rxjs/operators' | 2 | import { concatMap, map, switchMap, tap } from 'rxjs/operators' |
3 | import { Component, OnDestroy, OnInit } from '@angular/core' | 3 | import { Component, OnDestroy, OnInit } from '@angular/core' |
4 | import { ComponentPagination, hasMoreItems, ScreenService, User, UserService } from '@app/core' | 4 | import { ComponentPagination, hasMoreItems, MarkdownService, ScreenService, User, UserService } from '@app/core' |
5 | import { Account, AccountService, Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' | 5 | import { Account, AccountService, Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' |
6 | import { NSFWPolicyType, VideoSortField } from '@shared/models' | 6 | import { NSFWPolicyType, VideoSortField } from '@shared/models' |
7 | import { MiniatureDisplayOptions } from '@app/shared/shared-video-miniature' | ||
7 | 8 | ||
8 | @Component({ | 9 | @Component({ |
9 | selector: 'my-account-video-channels', | 10 | selector: 'my-account-video-channels', |
@@ -13,7 +14,10 @@ import { NSFWPolicyType, VideoSortField } from '@shared/models' | |||
13 | export class AccountVideoChannelsComponent implements OnInit, OnDestroy { | 14 | export class AccountVideoChannelsComponent implements OnInit, OnDestroy { |
14 | account: Account | 15 | account: Account |
15 | videoChannels: VideoChannel[] = [] | 16 | videoChannels: VideoChannel[] = [] |
16 | videos: { [id: number]: Video[] } = {} | 17 | |
18 | videos: { [id: number]: { total: number, videos: Video[] } } = {} | ||
19 | |||
20 | channelsDescriptionHTML: { [ id: number ]: string } = {} | ||
17 | 21 | ||
18 | channelPagination: ComponentPagination = { | 22 | channelPagination: ComponentPagination = { |
19 | currentPage: 1, | 23 | currentPage: 1, |
@@ -23,7 +27,7 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy { | |||
23 | 27 | ||
24 | videosPagination: ComponentPagination = { | 28 | videosPagination: ComponentPagination = { |
25 | currentPage: 1, | 29 | currentPage: 1, |
26 | itemsPerPage: 12, | 30 | itemsPerPage: 5, |
27 | totalItems: null | 31 | totalItems: null |
28 | } | 32 | } |
29 | videosSort: VideoSortField = '-publishedAt' | 33 | videosSort: VideoSortField = '-publishedAt' |
@@ -32,6 +36,16 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy { | |||
32 | 36 | ||
33 | userMiniature: User | 37 | userMiniature: User |
34 | nsfwPolicy: NSFWPolicyType | 38 | nsfwPolicy: NSFWPolicyType |
39 | miniatureDisplayOptions: MiniatureDisplayOptions = { | ||
40 | date: true, | ||
41 | views: true, | ||
42 | by: false, | ||
43 | avatar: false, | ||
44 | privacyLabel: false, | ||
45 | privacyText: false, | ||
46 | state: false, | ||
47 | blacklistInfo: false | ||
48 | } | ||
35 | 49 | ||
36 | private accountSub: Subscription | 50 | private accountSub: Subscription |
37 | 51 | ||
@@ -39,7 +53,7 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy { | |||
39 | private accountService: AccountService, | 53 | private accountService: AccountService, |
40 | private videoChannelService: VideoChannelService, | 54 | private videoChannelService: VideoChannelService, |
41 | private videoService: VideoService, | 55 | private videoService: VideoService, |
42 | private screenService: ScreenService, | 56 | private markdown: MarkdownService, |
43 | private userService: UserService | 57 | private userService: UserService |
44 | ) { } | 58 | ) { } |
45 | 59 | ||
@@ -78,23 +92,36 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy { | |||
78 | } | 92 | } |
79 | 93 | ||
80 | return this.videoService.getVideoChannelVideos(options) | 94 | return this.videoService.getVideoChannelVideos(options) |
81 | .pipe(map(data => ({ videoChannel, videos: data.data }))) | 95 | .pipe(map(data => ({ videoChannel, videos: data.data, total: data.total }))) |
82 | }) | 96 | }) |
83 | ) | 97 | ) |
84 | .subscribe(({ videoChannel, videos }) => { | 98 | .subscribe(async ({ videoChannel, videos, total }) => { |
99 | this.channelsDescriptionHTML[videoChannel.id] = await this.markdown.textMarkdownToHTML(videoChannel.description) | ||
100 | |||
85 | this.videoChannels.push(videoChannel) | 101 | this.videoChannels.push(videoChannel) |
86 | 102 | ||
87 | this.videos[videoChannel.id] = videos | 103 | this.videos[videoChannel.id] = { videos, total } |
88 | 104 | ||
89 | this.onChannelDataSubject.next([ videoChannel ]) | 105 | this.onChannelDataSubject.next([ videoChannel ]) |
90 | }) | 106 | }) |
91 | } | 107 | } |
92 | 108 | ||
93 | getVideosOf (videoChannel: VideoChannel) { | 109 | getVideosOf (videoChannel: VideoChannel) { |
94 | const numberOfVideos = this.screenService.getNumberOfAvailableMiniatures() | 110 | const obj = this.videos[ videoChannel.id ] |
111 | if (!obj) return [] | ||
112 | |||
113 | return obj.videos | ||
114 | } | ||
115 | |||
116 | getTotalVideosOf (videoChannel: VideoChannel) { | ||
117 | const obj = this.videos[ videoChannel.id ] | ||
118 | if (!obj) return undefined | ||
119 | |||
120 | return obj.total | ||
121 | } | ||
95 | 122 | ||
96 | // 2 rows | 123 | getChannelDescription (videoChannel: VideoChannel) { |
97 | return this.videos[ videoChannel.id ].slice(0, numberOfVideos * 2) | 124 | return this.channelsDescriptionHTML[videoChannel.id] |
98 | } | 125 | } |
99 | 126 | ||
100 | onNearOfBottom () { | 127 | onNearOfBottom () { |
diff --git a/client/src/app/+accounts/account-videos/account-videos.component.ts b/client/src/app/+accounts/account-videos/account-videos.component.ts index 484d60e25..75af45e90 100644 --- a/client/src/app/+accounts/account-videos/account-videos.component.ts +++ b/client/src/app/+accounts/account-videos/account-videos.component.ts | |||
@@ -16,6 +16,7 @@ import { VideoFilter } from '@shared/models' | |||
16 | ] | 16 | ] |
17 | }) | 17 | }) |
18 | export class AccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { | 18 | export class AccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { |
19 | // No value because we don't want a page title | ||
19 | titlePage: string | 20 | titlePage: string |
20 | loadOnInit = false | 21 | loadOnInit = false |
21 | loadUserVideoPreferences = true | 22 | loadUserVideoPreferences = true |
@@ -77,11 +78,6 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit, | |||
77 | 78 | ||
78 | return this.videoService | 79 | return this.videoService |
79 | .getAccountVideos(options) | 80 | .getAccountVideos(options) |
80 | .pipe( | ||
81 | tap(({ total }) => { | ||
82 | this.titlePage = $localize`Published ${total} videos` | ||
83 | }) | ||
84 | ) | ||
85 | } | 81 | } |
86 | 82 | ||
87 | toggleModerationDisplay () { | 83 | toggleModerationDisplay () { |
@@ -93,4 +89,8 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit, | |||
93 | generateSyndicationList () { | 89 | generateSyndicationList () { |
94 | this.syndicationItems = this.videoService.getAccountFeedUrls(this.account.id) | 90 | this.syndicationItems = this.videoService.getAccountFeedUrls(this.account.id) |
95 | } | 91 | } |
92 | |||
93 | displayAsRow () { | ||
94 | return this.screenService.isInMobileView() | ||
95 | } | ||
96 | } | 96 | } |
diff --git a/client/src/app/+accounts/accounts-routing.module.ts b/client/src/app/+accounts/accounts-routing.module.ts index 15937a67b..3bf0f7185 100644 --- a/client/src/app/+accounts/accounts-routing.module.ts +++ b/client/src/app/+accounts/accounts-routing.module.ts | |||
@@ -1,11 +1,10 @@ | |||
1 | import { NgModule } from '@angular/core' | 1 | import { NgModule } from '@angular/core' |
2 | import { RouterModule, Routes } from '@angular/router' | 2 | import { RouterModule, Routes } from '@angular/router' |
3 | import { MetaGuard } from '@ngx-meta/core' | 3 | import { MetaGuard } from '@ngx-meta/core' |
4 | import { AccountsComponent } from './accounts.component' | ||
5 | import { AccountVideosComponent } from './account-videos/account-videos.component' | ||
6 | import { AccountAboutComponent } from './account-about/account-about.component' | ||
7 | import { AccountVideoChannelsComponent } from './account-video-channels/account-video-channels.component' | ||
8 | import { AccountSearchComponent } from './account-search/account-search.component' | 4 | import { AccountSearchComponent } from './account-search/account-search.component' |
5 | import { AccountVideoChannelsComponent } from './account-video-channels/account-video-channels.component' | ||
6 | import { AccountVideosComponent } from './account-videos/account-videos.component' | ||
7 | import { AccountsComponent } from './accounts.component' | ||
9 | 8 | ||
10 | const accountsRoutes: Routes = [ | 9 | const accountsRoutes: Routes = [ |
11 | { | 10 | { |
@@ -32,15 +31,6 @@ const accountsRoutes: Routes = [ | |||
32 | } | 31 | } |
33 | }, | 32 | }, |
34 | { | 33 | { |
35 | path: 'about', | ||
36 | component: AccountAboutComponent, | ||
37 | data: { | ||
38 | meta: { | ||
39 | title: $localize`About account` | ||
40 | } | ||
41 | } | ||
42 | }, | ||
43 | { | ||
44 | path: 'videos', | 34 | path: 'videos', |
45 | component: AccountVideosComponent, | 35 | component: AccountVideosComponent, |
46 | data: { | 36 | data: { |
diff --git a/client/src/app/+accounts/accounts.component.html b/client/src/app/+accounts/accounts.component.html index 5bd7b0824..03d083bb6 100644 --- a/client/src/app/+accounts/accounts.component.html +++ b/client/src/app/+accounts/accounts.component.html | |||
@@ -1,57 +1,89 @@ | |||
1 | <div *ngIf="account" class="row"> | 1 | <div *ngIf="account" class="root"> |
2 | <div class="sub-menu"> | 2 | <div class="account-info"> |
3 | 3 | ||
4 | <div class="actor"> | 4 | <div class="account-avatar-row"> |
5 | <img [src]="account.avatarUrl" alt="Avatar" /> | 5 | <img class="account-avatar" [src]="account.avatarUrl" alt="Avatar" /> |
6 | 6 | ||
7 | <div class="actor-info"> | 7 | <div> |
8 | <div class="actor-names"> | 8 | <div class="section-label" i18n>PEERTUBE ACCOUNT</div> |
9 | <div class="actor-display-name">{{ account.displayName }}</div> | 9 | |
10 | <div class="actor-name"> | 10 | <div class="actor-info"> |
11 | <span>{{ account.nameWithHost }}</span> | 11 | <div> |
12 | <button [cdkCopyToClipboard]="account.nameWithHostForced" (click)="activateCopiedMessage()" | 12 | <div class="actor-display-name"> |
13 | class="btn btn-outline-secondary btn-sm copy-button" | 13 | <h1>{{ account.displayName }}</h1> |
14 | > | 14 | |
15 | <span class="glyphicon glyphicon-copy"></span> | 15 | <my-user-moderation-dropdown |
16 | </button> | 16 | [prependActions]="prependModerationActions" |
17 | buttonSize="small" [account]="account" [user]="accountUser" placement="bottom-left auto" | ||
18 | (userChanged)="onUserChanged()" (userDeleted)="onUserDeleted()" | ||
19 | ></my-user-moderation-dropdown> | ||
20 | |||
21 | <span *ngIf="accountUser?.blocked" [ngbTooltip]="accountUser.blockedReason" class="badge badge-danger" i18n>Banned</span> | ||
22 | <span *ngIf="account.mutedByUser" class="badge badge-danger" i18n>Muted</span> | ||
23 | <span *ngIf="account.mutedServerByUser" class="badge badge-danger" i18n>Instance muted</span> | ||
24 | <span *ngIf="account.mutedByInstance" class="badge badge-danger" i18n>Muted by your instance</span> | ||
25 | <span *ngIf="account.mutedServerByInstance" class="badge badge-danger" i18n>Instance muted by your instance</span> | ||
26 | </div> | ||
27 | |||
28 | <div class="actor-handle"> | ||
29 | <span>@{{ account.nameWithHost }}</span> | ||
30 | <button [cdkCopyToClipboard]="account.nameWithHostForced" (click)="activateCopiedMessage()" | ||
31 | class="btn btn-outline-secondary btn-sm copy-button" title="Copy account handle" i18n-title | ||
32 | > | ||
33 | <span class="glyphicon glyphicon-duplicate"></span> | ||
34 | </button> | ||
35 | </div> | ||
36 | |||
37 | <div class="actor-counters"> | ||
38 | <span i18n>{naiveAggregatedSubscribers(), plural, =1 {1 subscriber} other {{{ naiveAggregatedSubscribers() }} subscribers}}</span> | ||
39 | |||
40 | <span class="videos-count" *ngIf="accountVideosCount !== undefined" i18n> | ||
41 | {accountVideosCount, plural, =1 {1 videos} other {{{ accountVideosCount }} videos}} | ||
42 | </span> | ||
43 | </div> | ||
17 | </div> | 44 | </div> |
18 | <span *ngIf="accountUser?.blocked" [ngbTooltip]="accountUser.blockedReason" class="badge badge-danger" i18n>Banned</span> | ||
19 | <span *ngIf="account.mutedByUser" class="badge badge-danger" i18n>Muted</span> | ||
20 | <span *ngIf="account.mutedServerByUser" class="badge badge-danger" i18n>Instance muted</span> | ||
21 | <span *ngIf="account.mutedByInstance" class="badge badge-danger" i18n>Muted by your instance</span> | ||
22 | <span *ngIf="account.mutedServerByInstance" class="badge badge-danger" i18n>Instance muted by your instance</span> | ||
23 | |||
24 | <my-user-moderation-dropdown | ||
25 | [prependActions]="prependModerationActions" | ||
26 | buttonSize="small" [account]="account" [user]="accountUser" placement="bottom-left auto" | ||
27 | (userChanged)="onUserChanged()" (userDeleted)="onUserDeleted()" | ||
28 | ></my-user-moderation-dropdown> | ||
29 | </div> | ||
30 | <div class="actor-followers" [title]="accountFollowerTitle"> | ||
31 | {{ subscribersDisplayFor(naiveAggregatedSubscribers) }} | ||
32 | </div> | 45 | </div> |
33 | </div> | 46 | </div> |
47 | </div> | ||
34 | 48 | ||
35 | <div class="right-buttons"> | 49 | <div class="description" [ngClass]="{ expanded: accountDescriptionExpanded }"> |
36 | <a *ngIf="isAccountManageable && !isInSmallView" routerLink="/my-account" class="btn btn-outline-tertiary mr-2" i18n>Manage account</a> | 50 | <div class="description-html" [innerHTML]="accountDescriptionHTML"></div> |
37 | <my-subscribe-button *ngIf="videoChannels" [account]="account" [videoChannels]="videoChannels"></my-subscribe-button> | 51 | |
38 | </div> | 52 | <div class="created-at" i18n>Account created on {{ account.createdAt | date }}</div> |
39 | </div> | 53 | </div> |
40 | 54 | ||
41 | <div class="links w-100"> | 55 | <div *ngIf="hasShowMoreDescription()" class="show-more" role="button" |
42 | <ng-template #linkTemplate let-item="item"> | 56 | (click)="accountDescriptionExpanded = !accountDescriptionExpanded" |
43 | <a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a> | 57 | title="Show the complete description" i18n-title i18n |
44 | </ng-template> | 58 | > |
59 | Show more... | ||
60 | </div> | ||
45 | 61 | ||
46 | <list-overflow [items]="links" [itemTemplate]="linkTemplate"></list-overflow> | 62 | <div class="buttons"> |
63 | <a *ngIf="isManageable()" routerLink="/my-account" class="peertube-button-link orange-button" i18n> | ||
64 | Manage account | ||
65 | </a> | ||
47 | 66 | ||
48 | <simple-search-input (searchChanged)="searchChanged($event)" name="search-videos" i18n-placeholder placeholder="Search videos"></simple-search-input> | 67 | <my-subscribe-button *ngIf="hasVideoChannels() && !isManageable()" [account]="account" [videoChannels]="videoChannels"></my-subscribe-button> |
49 | </div> | 68 | </div> |
50 | </div> | 69 | </div> |
51 | 70 | ||
52 | <div class="margin-content"> | 71 | <div class="links"> |
53 | <router-outlet (activate)="onOutletLoaded($event)"></router-outlet> | 72 | <ng-template #linkTemplate let-item="item"> |
73 | <a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a> | ||
74 | </ng-template> | ||
75 | |||
76 | <list-overflow [hidden]="hideMenu" [items]="links" [itemTemplate]="linkTemplate"></list-overflow> | ||
77 | |||
78 | <simple-search-input | ||
79 | [alwaysShow]="!isInSmallView()" (searchChanged)="searchChanged($event)" | ||
80 | (inputDisplayChanged)="onSearchInputDisplayChanged($event)" name="search-videos" | ||
81 | i18n-iconTitle icon-title="Search account videos" | ||
82 | i18n-placeholder placeholder="Search account videos" | ||
83 | ></simple-search-input> | ||
54 | </div> | 84 | </div> |
85 | |||
86 | <router-outlet (activate)="onOutletLoaded($event)"></router-outlet> | ||
55 | </div> | 87 | </div> |
56 | 88 | ||
57 | <ng-container *ngIf="prependModerationActions"> | 89 | <ng-container *ngIf="prependModerationActions"> |
diff --git a/client/src/app/+accounts/accounts.component.scss b/client/src/app/+accounts/accounts.component.scss index 40c6b6493..a836e84ce 100644 --- a/client/src/app/+accounts/accounts.component.scss +++ b/client/src/app/+accounts/accounts.component.scss | |||
@@ -1,48 +1,29 @@ | |||
1 | // Bootstrap grid utilities require functions, variables and mixins | ||
2 | @import 'node_modules/bootstrap/scss/functions'; | ||
3 | @import 'node_modules/bootstrap/scss/variables'; | ||
4 | @import 'node_modules/bootstrap/scss/mixins'; | ||
5 | @import 'node_modules/bootstrap/scss/grid'; | ||
6 | |||
7 | @import '_variables'; | 1 | @import '_variables'; |
8 | @import '_mixins'; | 2 | @import '_mixins'; |
9 | 3 | @import '_actor'; | |
10 | .sub-menu { | 4 | @import '_miniature'; |
11 | @include sub-menu-with-actor; | 5 | |
12 | 6 | .root { | |
13 | .actor { | 7 | --myGlobalTopPadding: 60px; |
14 | width: 100%; | 8 | --myImgMargin: 30px; |
15 | } | 9 | --myFontSize: 16px; |
10 | --myGreyFontSize: 16px; | ||
16 | } | 11 | } |
17 | 12 | ||
18 | .margin-content { | 13 | .section-label { |
19 | // margin-content is required, but child views have their own margins | 14 | @include section-label-responsive; |
20 | // that match views outside the scope of accounts, so we only align | ||
21 | // them with the margins of .sub-menu when required. | ||
22 | margin: 0; | ||
23 | } | 15 | } |
24 | 16 | ||
25 | .right-buttons { | 17 | .links { |
26 | display: flex; | 18 | @include grid-videos-miniature-margins; |
27 | height: max-content; | ||
28 | margin-left: auto; | ||
29 | margin-top: 10px; | ||
30 | |||
31 | @include media-breakpoint-down(lg) { | ||
32 | flex-flow: column-reverse; | ||
33 | 19 | ||
34 | a { | 20 | display: flex; |
35 | margin-top: 0.25rem; | 21 | justify-content: space-between; |
36 | margin-right: 0 !important; | 22 | align-items: center; |
37 | } | 23 | max-width: $max-channels-width; |
38 | } | ||
39 | |||
40 | a { | ||
41 | @include peertube-button-outline; | ||
42 | } | ||
43 | 24 | ||
44 | my-subscribe-button { | 25 | simple-search-input { |
45 | min-height: 30px; | 26 | margin-left: auto; |
46 | } | 27 | } |
47 | } | 28 | } |
48 | 29 | ||
@@ -60,39 +41,106 @@ my-user-moderation-dropdown, | |||
60 | 41 | ||
61 | .copy-button { | 42 | .copy-button { |
62 | border: none; | 43 | border: none; |
63 | padding: 5px; | 44 | } |
64 | margin-top: -2px; | 45 | |
46 | .account-info { | ||
47 | @include grid-videos-miniature-margins(false, 15px); | ||
48 | |||
49 | display: grid; | ||
50 | grid-template-columns: 1fr min-content; | ||
51 | grid-template-rows: auto auto; | ||
52 | |||
53 | background-color: pvar(--submenuBackgroundColor); | ||
54 | margin-bottom: 45px; | ||
55 | padding-top: var(--myGlobalTopPadding); | ||
56 | font-size: var(--myFontSize); | ||
57 | } | ||
58 | |||
59 | .account-avatar-row { | ||
60 | @include avatar-row-responsive(var(--myImgMargin), var(--myGreyFontSize)); | ||
61 | } | ||
62 | |||
63 | .description { | ||
64 | grid-column: 1 / 3; | ||
65 | max-width: 1000px; | ||
66 | word-break: break-word; | ||
67 | } | ||
68 | |||
69 | .created-at { | ||
70 | margin-top: 15px; | ||
71 | color: pvar(--greyForegroundColor); | ||
72 | padding-bottom: 60px; | ||
73 | } | ||
74 | |||
75 | .show-more { | ||
76 | @include show-more-description; | ||
77 | |||
78 | display: none; | ||
79 | text-align: center; | ||
80 | } | ||
81 | |||
82 | .buttons { | ||
83 | grid-column: 2; | ||
84 | grid-row: 1; | ||
85 | |||
86 | display: flex; | ||
87 | flex-wrap: wrap; | ||
88 | justify-content: flex-end; | ||
89 | align-content: flex-start; | ||
90 | |||
91 | > *:not(:last-child) { | ||
92 | margin-bottom: 15px; | ||
93 | } | ||
94 | |||
95 | > a { | ||
96 | white-space: nowrap; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | @media screen and (max-width: $small-view) { | ||
101 | .root { | ||
102 | --myGlobalTopPadding: 45px; | ||
103 | --myChannelImgMargin: 15px; | ||
104 | } | ||
105 | |||
106 | .account-info { | ||
107 | display: block; | ||
108 | padding-bottom: 60px; | ||
109 | } | ||
110 | |||
111 | .description:not(.expanded) { | ||
112 | max-height: 70px; | ||
113 | |||
114 | @include fade-text(30px, pvar(--submenuBackgroundColor)); | ||
115 | } | ||
116 | |||
117 | .show-more { | ||
118 | display: block; | ||
119 | } | ||
120 | |||
121 | .buttons { | ||
122 | justify-content: center; | ||
123 | } | ||
65 | } | 124 | } |
66 | 125 | ||
67 | @media screen and (max-width: $mobile-view) { | 126 | @media screen and (max-width: $mobile-view) { |
68 | .sub-menu { | 127 | .root { |
69 | .actor { | 128 | --myGlobalTopPadding: 15px; |
70 | flex-direction: column; | 129 | --myFontSize: 14px; |
71 | align-items: center; | 130 | --myGreyFontSize: 13px; |
72 | 131 | } | |
73 | img, | 132 | |
74 | .actor-info .actor-names .actor-display-name { | 133 | .account-info { |
75 | margin-right: 0; | 134 | display: block; |
76 | } | 135 | padding-bottom: 30px; |
77 | 136 | } | |
78 | .actor-info { | 137 | |
79 | .actor-names { | 138 | .links { |
80 | flex-direction: column; | 139 | margin: auto !important; |
81 | align-items: center; | 140 | width: min-content; |
82 | } | 141 | } |
83 | 142 | ||
84 | my-user-moderation-dropdown { | 143 | .show-more { |
85 | margin-left: 0; | 144 | margin-bottom: 30px; |
86 | } | ||
87 | |||
88 | .actor-followers { | ||
89 | text-align: center; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | .right-buttons { | ||
94 | margin-left: 0; | ||
95 | } | ||
96 | } | ||
97 | } | 145 | } |
98 | } | 146 | } |
diff --git a/client/src/app/+accounts/accounts.component.ts b/client/src/app/+accounts/accounts.component.ts index e6a5a5d5e..fbd7380a9 100644 --- a/client/src/app/+accounts/accounts.component.ts +++ b/client/src/app/+accounts/accounts.component.ts | |||
@@ -2,11 +2,19 @@ import { Subscription } from 'rxjs' | |||
2 | import { catchError, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators' | 2 | import { catchError, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators' |
3 | import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core' | 3 | import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core' |
4 | import { ActivatedRoute } from '@angular/router' | 4 | import { ActivatedRoute } from '@angular/router' |
5 | import { AuthService, Notifier, RedirectService, RestExtractor, ScreenService, UserService } from '@app/core' | 5 | import { AuthService, MarkdownService, Notifier, RedirectService, RestExtractor, ScreenService, UserService } from '@app/core' |
6 | import { Account, AccountService, DropdownAction, ListOverflowItem, VideoChannel, VideoChannelService } from '@app/shared/shared-main' | 6 | import { |
7 | Account, | ||
8 | AccountService, | ||
9 | DropdownAction, | ||
10 | ListOverflowItem, | ||
11 | VideoChannel, | ||
12 | VideoChannelService, | ||
13 | VideoService | ||
14 | } from '@app/shared/shared-main' | ||
7 | import { AccountReportComponent } from '@app/shared/shared-moderation' | 15 | import { AccountReportComponent } from '@app/shared/shared-moderation' |
8 | import { User, UserRight } from '@shared/models' | ||
9 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | 16 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' |
17 | import { User, UserRight } from '@shared/models' | ||
10 | import { AccountSearchComponent } from './account-search/account-search.component' | 18 | import { AccountSearchComponent } from './account-search/account-search.component' |
11 | 19 | ||
12 | @Component({ | 20 | @Component({ |
@@ -15,16 +23,23 @@ import { AccountSearchComponent } from './account-search/account-search.componen | |||
15 | }) | 23 | }) |
16 | export class AccountsComponent implements OnInit, OnDestroy { | 24 | export class AccountsComponent implements OnInit, OnDestroy { |
17 | @ViewChild('accountReportModal') accountReportModal: AccountReportComponent | 25 | @ViewChild('accountReportModal') accountReportModal: AccountReportComponent |
26 | |||
18 | accountSearch: AccountSearchComponent | 27 | accountSearch: AccountSearchComponent |
19 | 28 | ||
20 | account: Account | 29 | account: Account |
21 | accountUser: User | 30 | accountUser: User |
31 | |||
22 | videoChannels: VideoChannel[] = [] | 32 | videoChannels: VideoChannel[] = [] |
33 | |||
23 | links: ListOverflowItem[] = [] | 34 | links: ListOverflowItem[] = [] |
35 | hideMenu = false | ||
24 | 36 | ||
25 | isAccountManageable = false | ||
26 | accountFollowerTitle = '' | 37 | accountFollowerTitle = '' |
27 | 38 | ||
39 | accountVideosCount: number | ||
40 | accountDescriptionHTML = '' | ||
41 | accountDescriptionExpanded = false | ||
42 | |||
28 | prependModerationActions: DropdownAction<any>[] | 43 | prependModerationActions: DropdownAction<any>[] |
29 | 44 | ||
30 | private routeSub: Subscription | 45 | private routeSub: Subscription |
@@ -38,6 +53,8 @@ export class AccountsComponent implements OnInit, OnDestroy { | |||
38 | private restExtractor: RestExtractor, | 53 | private restExtractor: RestExtractor, |
39 | private redirectService: RedirectService, | 54 | private redirectService: RedirectService, |
40 | private authService: AuthService, | 55 | private authService: AuthService, |
56 | private videoService: VideoService, | ||
57 | private markdown: MarkdownService, | ||
41 | private screenService: ScreenService | 58 | private screenService: ScreenService |
42 | ) { | 59 | ) { |
43 | } | 60 | } |
@@ -62,9 +79,8 @@ export class AccountsComponent implements OnInit, OnDestroy { | |||
62 | ) | 79 | ) |
63 | 80 | ||
64 | this.links = [ | 81 | this.links = [ |
65 | { label: $localize`VIDEO CHANNELS`, routerLink: 'video-channels' }, | 82 | { label: $localize`CHANNELS`, routerLink: 'video-channels' }, |
66 | { label: $localize`VIDEOS`, routerLink: 'videos' }, | 83 | { label: $localize`VIDEOS`, routerLink: 'videos' } |
67 | { label: $localize`ABOUT`, routerLink: 'about' } | ||
68 | ] | 84 | ] |
69 | } | 85 | } |
70 | 86 | ||
@@ -72,19 +88,29 @@ export class AccountsComponent implements OnInit, OnDestroy { | |||
72 | if (this.routeSub) this.routeSub.unsubscribe() | 88 | if (this.routeSub) this.routeSub.unsubscribe() |
73 | } | 89 | } |
74 | 90 | ||
75 | get naiveAggregatedSubscribers () { | 91 | naiveAggregatedSubscribers () { |
76 | return this.videoChannels.reduce( | 92 | return this.videoChannels.reduce( |
77 | (acc, val) => acc + val.followersCount, | 93 | (acc, val) => acc + val.followersCount, |
78 | this.account.followersCount // accumulator starts with the base number of subscribers the account has | 94 | this.account.followersCount // accumulator starts with the base number of subscribers the account has |
79 | ) | 95 | ) |
80 | } | 96 | } |
81 | 97 | ||
82 | get isInSmallView () { | 98 | isUserLoggedIn () { |
99 | return this.authService.isLoggedIn() | ||
100 | } | ||
101 | |||
102 | isInSmallView () { | ||
83 | return this.screenService.isInSmallView() | 103 | return this.screenService.isInSmallView() |
84 | } | 104 | } |
85 | 105 | ||
106 | isManageable () { | ||
107 | if (!this.isUserLoggedIn()) return false | ||
108 | |||
109 | return this.account?.userId === this.authService.getUser().id | ||
110 | } | ||
111 | |||
86 | onUserChanged () { | 112 | onUserChanged () { |
87 | this.getUserIfNeeded(this.account) | 113 | this.loadUserIfNeeded(this.account) |
88 | } | 114 | } |
89 | 115 | ||
90 | onUserDeleted () { | 116 | onUserDeleted () { |
@@ -113,40 +139,38 @@ export class AccountsComponent implements OnInit, OnDestroy { | |||
113 | if (this.accountSearch) this.accountSearch.updateSearch(search) | 139 | if (this.accountSearch) this.accountSearch.updateSearch(search) |
114 | } | 140 | } |
115 | 141 | ||
116 | private onAccount (account: Account) { | 142 | onSearchInputDisplayChanged (displayed: boolean) { |
143 | this.hideMenu = this.isInSmallView() && displayed | ||
144 | } | ||
145 | |||
146 | hasVideoChannels () { | ||
147 | return this.videoChannels.length !== 0 | ||
148 | } | ||
149 | |||
150 | hasShowMoreDescription () { | ||
151 | return !this.accountDescriptionExpanded && this.accountDescriptionHTML.length > 100 | ||
152 | } | ||
153 | |||
154 | private async onAccount (account: Account) { | ||
155 | this.accountFollowerTitle = $localize`${account.followersCount} direct account followers` | ||
156 | |||
117 | this.prependModerationActions = undefined | 157 | this.prependModerationActions = undefined |
118 | 158 | ||
119 | this.account = account | 159 | this.accountDescriptionHTML = await this.markdown.textMarkdownToHTML(account.description) |
120 | 160 | ||
121 | if (this.authService.isLoggedIn()) { | 161 | // After the markdown renderer to avoid layout changes |
122 | this.authService.userInformationLoaded.subscribe( | 162 | this.account = account |
123 | () => { | ||
124 | this.isAccountManageable = this.account.userId && this.account.userId === this.authService.getUser().id | ||
125 | |||
126 | const followers = this.subscribersDisplayFor(account.followersCount) | ||
127 | this.accountFollowerTitle = $localize`${followers} direct account followers` | ||
128 | |||
129 | // It's not our account, we can report it | ||
130 | if (!this.isAccountManageable) { | ||
131 | this.prependModerationActions = [ | ||
132 | { | ||
133 | label: $localize`Report this account`, | ||
134 | handler: () => this.showReportModal() | ||
135 | } | ||
136 | ] | ||
137 | } | ||
138 | } | ||
139 | ) | ||
140 | } | ||
141 | 163 | ||
142 | this.getUserIfNeeded(account) | 164 | this.updateModerationActions() |
165 | this.loadUserIfNeeded(account) | ||
166 | this.loadAccountVideosCount() | ||
143 | } | 167 | } |
144 | 168 | ||
145 | private showReportModal () { | 169 | private showReportModal () { |
146 | this.accountReportModal.show() | 170 | this.accountReportModal.show() |
147 | } | 171 | } |
148 | 172 | ||
149 | private getUserIfNeeded (account: Account) { | 173 | private loadUserIfNeeded (account: Account) { |
150 | if (!account.userId || !this.authService.isLoggedIn()) return | 174 | if (!account.userId || !this.authService.isLoggedIn()) return |
151 | 175 | ||
152 | const user = this.authService.getUser() | 176 | const user = this.authService.getUser() |
@@ -158,4 +182,33 @@ export class AccountsComponent implements OnInit, OnDestroy { | |||
158 | ) | 182 | ) |
159 | } | 183 | } |
160 | } | 184 | } |
185 | |||
186 | private updateModerationActions () { | ||
187 | if (!this.authService.isLoggedIn()) return | ||
188 | |||
189 | this.authService.userInformationLoaded.subscribe( | ||
190 | () => { | ||
191 | if (this.isManageable()) return | ||
192 | |||
193 | // It's not our account, we can report it | ||
194 | this.prependModerationActions = [ | ||
195 | { | ||
196 | label: $localize`Report this account`, | ||
197 | handler: () => this.showReportModal() | ||
198 | } | ||
199 | ] | ||
200 | } | ||
201 | ) | ||
202 | } | ||
203 | |||
204 | private loadAccountVideosCount () { | ||
205 | this.videoService.getAccountVideos({ | ||
206 | account: this.account, | ||
207 | videoPagination: { | ||
208 | currentPage: 1, | ||
209 | itemsPerPage: 0 | ||
210 | }, | ||
211 | sort: '-publishedAt' | ||
212 | }).subscribe(res => this.accountVideosCount = res.total) | ||
213 | } | ||
161 | } | 214 | } |
diff --git a/client/src/app/+accounts/accounts.module.ts b/client/src/app/+accounts/accounts.module.ts index 6da65cbc1..3354b4189 100644 --- a/client/src/app/+accounts/accounts.module.ts +++ b/client/src/app/+accounts/accounts.module.ts | |||
@@ -5,10 +5,9 @@ import { SharedMainModule } from '@app/shared/shared-main' | |||
5 | import { SharedModerationModule } from '@app/shared/shared-moderation' | 5 | import { SharedModerationModule } from '@app/shared/shared-moderation' |
6 | import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription' | 6 | import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription' |
7 | import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature' | 7 | import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature' |
8 | import { AccountAboutComponent } from './account-about/account-about.component' | 8 | import { AccountSearchComponent } from './account-search/account-search.component' |
9 | import { AccountVideoChannelsComponent } from './account-video-channels/account-video-channels.component' | 9 | import { AccountVideoChannelsComponent } from './account-video-channels/account-video-channels.component' |
10 | import { AccountVideosComponent } from './account-videos/account-videos.component' | 10 | import { AccountVideosComponent } from './account-videos/account-videos.component' |
11 | import { AccountSearchComponent } from './account-search/account-search.component' | ||
12 | import { AccountsRoutingModule } from './accounts-routing.module' | 11 | import { AccountsRoutingModule } from './accounts-routing.module' |
13 | import { AccountsComponent } from './accounts.component' | 12 | import { AccountsComponent } from './accounts.component' |
14 | 13 | ||
@@ -28,7 +27,6 @@ import { AccountsComponent } from './accounts.component' | |||
28 | AccountsComponent, | 27 | AccountsComponent, |
29 | AccountVideosComponent, | 28 | AccountVideosComponent, |
30 | AccountVideoChannelsComponent, | 29 | AccountVideoChannelsComponent, |
31 | AccountAboutComponent, | ||
32 | AccountSearchComponent | 30 | AccountSearchComponent |
33 | ], | 31 | ], |
34 | 32 | ||
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts index fd648a425..bac65c88e 100644 --- a/client/src/app/+admin/admin.module.ts +++ b/client/src/app/+admin/admin.module.ts | |||
@@ -3,6 +3,7 @@ import { SelectButtonModule } from 'primeng/selectbutton' | |||
3 | import { TableModule } from 'primeng/table' | 3 | import { TableModule } from 'primeng/table' |
4 | import { NgModule } from '@angular/core' | 4 | import { NgModule } from '@angular/core' |
5 | import { SharedAbuseListModule } from '@app/shared/shared-abuse-list' | 5 | import { SharedAbuseListModule } from '@app/shared/shared-abuse-list' |
6 | import { SharedActorImageModule } from '@app/shared/shared-actor-image' | ||
6 | import { SharedFormModule } from '@app/shared/shared-forms' | 7 | import { SharedFormModule } from '@app/shared/shared-forms' |
7 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' | 8 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' |
8 | import { SharedMainModule } from '@app/shared/shared-main' | 9 | import { SharedMainModule } from '@app/shared/shared-main' |
@@ -49,6 +50,7 @@ import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersCom | |||
49 | SharedGlobalIconModule, | 50 | SharedGlobalIconModule, |
50 | SharedAbuseListModule, | 51 | SharedAbuseListModule, |
51 | SharedVideoCommentModule, | 52 | SharedVideoCommentModule, |
53 | SharedActorImageModule, | ||
52 | 54 | ||
53 | TableModule, | 55 | TableModule, |
54 | SelectButtonModule, | 56 | SelectButtonModule, |
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 82c371f4d..d6aca10e7 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 | |||
@@ -164,7 +164,8 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV | |||
164 | baseUrl: `${environment.originServerUrl}/videos/embed/${entry.video.uuid}`, | 164 | baseUrl: `${environment.originServerUrl}/videos/embed/${entry.video.uuid}`, |
165 | title: false, | 165 | title: false, |
166 | warningTitle: false | 166 | warningTitle: false |
167 | }) | 167 | }), |
168 | entry.video.name | ||
168 | ) | 169 | ) |
169 | } | 170 | } |
170 | 171 | ||
diff --git a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html index 1b5fe45c6..8edf03a89 100644 --- a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html +++ b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html | |||
@@ -3,7 +3,7 @@ | |||
3 | </div> | 3 | </div> |
4 | 4 | ||
5 | <div class="search-bar"> | 5 | <div class="search-bar"> |
6 | <input type="text" (input)="onSearchChange($event)" i18n-placeholder placeholder="Search..."/> | 6 | <input type="text" (input)="onSearchChange($event)" i18n-placeholder placeholder="Search..." autofocus /> |
7 | </div> | 7 | </div> |
8 | 8 | ||
9 | <div class="alert alert-info" i18n *ngIf="pluginInstalled"> | 9 | <div class="alert alert-info" i18n *ngIf="pluginInstalled"> |
@@ -20,8 +20,8 @@ | |||
20 | <my-global-icon iconName="search"></my-global-icon> | 20 | <my-global-icon iconName="search"></my-global-icon> |
21 | 21 | ||
22 | <ng-container i18n> | 22 | <ng-container i18n> |
23 | {{ pagination.totalItems }} {pagination.totalItems, plural, =1 {result} other {results}} for "{{ search }}" | 23 | {{ pagination.totalItems }} {pagination.totalItems, plural, =1 {result} other {results}} for {{ search }}" |
24 | </ng-container> | 24 | </ng-container> |
25 | </ng-container> | 25 | </ng-container> |
26 | </div> | 26 | </div> |
27 | 27 | ||
diff --git a/client/src/app/+admin/plugins/shared/plugin-list.component.scss b/client/src/app/+admin/plugins/shared/plugin-list.component.scss index 83030b7e0..f59a01b74 100644 --- a/client/src/app/+admin/plugins/shared/plugin-list.component.scss +++ b/client/src/app/+admin/plugins/shared/plugin-list.component.scss | |||
@@ -3,7 +3,7 @@ | |||
3 | 3 | ||
4 | .plugin { | 4 | .plugin { |
5 | margin: 15px 0; | 5 | margin: 15px 0; |
6 | background-color: pvar(--submenuColor); | 6 | background-color: pvar(--submenuBackgroundColor); |
7 | } | 7 | } |
8 | 8 | ||
9 | .first-row { | 9 | .first-row { |
diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.html b/client/src/app/+admin/users/user-edit/user-edit.component.html index 243c6556a..5e92c0f36 100644 --- a/client/src/app/+admin/users/user-edit/user-edit.component.html +++ b/client/src/app/+admin/users/user-edit/user-edit.component.html | |||
@@ -72,7 +72,7 @@ | |||
72 | <div class="anchor" id="user"></div> <!-- user anchor --> | 72 | <div class="anchor" id="user"></div> <!-- user anchor --> |
73 | <div *ngIf="isCreation()" class="account-title" i18n>NEW USER</div> | 73 | <div *ngIf="isCreation()" class="account-title" i18n>NEW USER</div> |
74 | <div *ngIf="!isCreation() && user" class="account-title"> | 74 | <div *ngIf="!isCreation() && user" class="account-title"> |
75 | <my-actor-avatar-info [actor]="user.account"></my-actor-avatar-info> | 75 | <my-actor-avatar-edit [actor]="user.account" [editable]="false" [displaySubscribers]="false" [displayUsername]="false"></my-actor-avatar-edit> |
76 | </div> | 76 | </div> |
77 | </div> | 77 | </div> |
78 | 78 | ||
diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.scss b/client/src/app/+admin/users/user-edit/user-edit.component.scss index aa87b8d6d..8b0ac8783 100644 --- a/client/src/app/+admin/users/user-edit/user-edit.component.scss +++ b/client/src/app/+admin/users/user-edit/user-edit.component.scss | |||
@@ -72,11 +72,3 @@ input[type=submit], button { | |||
72 | @include dashboard; | 72 | @include dashboard; |
73 | max-width: 900px; | 73 | max-width: 900px; |
74 | } | 74 | } |
75 | |||
76 | my-actor-avatar-info ::ng-deep { | ||
77 | .actor-img-edit-container, | ||
78 | .actor-info-followers, | ||
79 | .actor-info-username { | ||
80 | display: none; | ||
81 | } | ||
82 | } | ||
diff --git a/client/src/app/+login/login.component.html b/client/src/app/+login/login.component.html index 3171e5b0f..0167066a0 100644 --- a/client/src/app/+login/login.component.html +++ b/client/src/app/+login/login.component.html | |||
@@ -21,7 +21,7 @@ | |||
21 | <label i18n for="username">User</label> | 21 | <label i18n for="username">User</label> |
22 | <input | 22 | <input |
23 | type="text" id="username" i18n-placeholder placeholder="Username or email address" required tabindex="1" | 23 | type="text" id="username" i18n-placeholder placeholder="Username or email address" required tabindex="1" |
24 | formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" #usernameInput | 24 | formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" autofocus |
25 | > | 25 | > |
26 | </div> | 26 | </div> |
27 | 27 | ||
diff --git a/client/src/app/+login/login.component.ts b/client/src/app/+login/login.component.ts index af747b7fa..d8ad49081 100644 --- a/client/src/app/+login/login.component.ts +++ b/client/src/app/+login/login.component.ts | |||
@@ -3,9 +3,9 @@ import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angula | |||
3 | import { ActivatedRoute } from '@angular/router' | 3 | import { ActivatedRoute } from '@angular/router' |
4 | import { AuthService, Notifier, RedirectService, UserService } from '@app/core' | 4 | import { AuthService, Notifier, RedirectService, UserService } from '@app/core' |
5 | import { HooksService } from '@app/core/plugins/hooks.service' | 5 | import { HooksService } from '@app/core/plugins/hooks.service' |
6 | import { InstanceAboutAccordionComponent } from '@app/shared/shared-instance' | ||
7 | import { LOGIN_PASSWORD_VALIDATOR, LOGIN_USERNAME_VALIDATOR } from '@app/shared/form-validators/login-validators' | 6 | import { LOGIN_PASSWORD_VALIDATOR, LOGIN_USERNAME_VALIDATOR } from '@app/shared/form-validators/login-validators' |
8 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 7 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' |
8 | import { InstanceAboutAccordionComponent } from '@app/shared/shared-instance' | ||
9 | import { NgbAccordion, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' | 9 | import { NgbAccordion, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' |
10 | import { RegisteredExternalAuthConfig, ServerConfig } from '@shared/models' | 10 | import { RegisteredExternalAuthConfig, ServerConfig } from '@shared/models' |
11 | 11 | ||
@@ -16,7 +16,6 @@ import { RegisteredExternalAuthConfig, ServerConfig } from '@shared/models' | |||
16 | }) | 16 | }) |
17 | 17 | ||
18 | export class LoginComponent extends FormReactive implements OnInit, AfterViewInit { | 18 | export class LoginComponent extends FormReactive implements OnInit, AfterViewInit { |
19 | @ViewChild('usernameInput', { static: false }) usernameInput: ElementRef | ||
20 | @ViewChild('forgotPasswordModal', { static: true }) forgotPasswordModal: ElementRef | 19 | @ViewChild('forgotPasswordModal', { static: true }) forgotPasswordModal: ElementRef |
21 | 20 | ||
22 | accordion: NgbAccordion | 21 | accordion: NgbAccordion |
@@ -91,10 +90,6 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni | |||
91 | } | 90 | } |
92 | 91 | ||
93 | ngAfterViewInit () { | 92 | ngAfterViewInit () { |
94 | if (this.usernameInput) { | ||
95 | this.usernameInput.nativeElement.focus() | ||
96 | } | ||
97 | |||
98 | this.hooks.runAction('action:login.init', 'login') | 93 | this.hooks.runAction('action:login.init', 'login') |
99 | } | 94 | } |
100 | 95 | ||
diff --git a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts index ad7497f45..c7e173038 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts | |||
@@ -42,7 +42,9 @@ export class MyAccountNotificationPreferencesComponent implements OnInit { | |||
42 | newInstanceFollower: $localize`Your instance has a new follower`, | 42 | newInstanceFollower: $localize`Your instance has a new follower`, |
43 | autoInstanceFollowing: $localize`Your instance automatically followed another instance`, | 43 | autoInstanceFollowing: $localize`Your instance automatically followed another instance`, |
44 | abuseNewMessage: $localize`An abuse report received a new message`, | 44 | abuseNewMessage: $localize`An abuse report received a new message`, |
45 | abuseStateChange: $localize`One of your abuse reports has been accepted or rejected by moderators` | 45 | abuseStateChange: $localize`One of your abuse reports has been accepted or rejected by moderators`, |
46 | newPeerTubeVersion: $localize`A new PeerTube version is available`, | ||
47 | newPluginVersion: $localize`One of your plugin/theme has a new available version` | ||
46 | } | 48 | } |
47 | this.notificationSettingKeys = Object.keys(this.labelNotifications) as (keyof UserNotificationSetting)[] | 49 | this.notificationSettingKeys = Object.keys(this.labelNotifications) as (keyof UserNotificationSetting)[] |
48 | 50 | ||
@@ -51,7 +53,9 @@ export class MyAccountNotificationPreferencesComponent implements OnInit { | |||
51 | videoAutoBlacklistAsModerator: UserRight.MANAGE_VIDEO_BLACKLIST, | 53 | videoAutoBlacklistAsModerator: UserRight.MANAGE_VIDEO_BLACKLIST, |
52 | newUserRegistration: UserRight.MANAGE_USERS, | 54 | newUserRegistration: UserRight.MANAGE_USERS, |
53 | newInstanceFollower: UserRight.MANAGE_SERVER_FOLLOW, | 55 | newInstanceFollower: UserRight.MANAGE_SERVER_FOLLOW, |
54 | autoInstanceFollowing: UserRight.MANAGE_CONFIGURATION | 56 | autoInstanceFollowing: UserRight.MANAGE_CONFIGURATION, |
57 | newPeerTubeVersion: UserRight.MANAGE_DEBUG, | ||
58 | newPluginVersion: UserRight.MANAGE_DEBUG | ||
55 | } | 59 | } |
56 | } | 60 | } |
57 | 61 | ||
diff --git a/client/src/app/+my-account/my-account-settings/my-account-settings.component.html b/client/src/app/+my-account/my-account-settings/my-account-settings.component.html index b0d2ec58d..48d06280b 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-settings.component.html +++ b/client/src/app/+my-account/my-account-settings/my-account-settings.component.html | |||
@@ -3,7 +3,7 @@ | |||
3 | <div class="form-group col-12 col-lg-4 col-xl-3"></div> | 3 | <div class="form-group col-12 col-lg-4 col-xl-3"></div> |
4 | 4 | ||
5 | <div class="form-group col-12 col-lg-8 col-xl-9"> | 5 | <div class="form-group col-12 col-lg-8 col-xl-9"> |
6 | <my-actor-avatar-info [actor]="user.account" (avatarChange)="onAvatarChange($event)" (avatarDelete)="onAvatarDelete()"></my-actor-avatar-info> | 6 | <my-actor-avatar-edit [actor]="user.account" (avatarChange)="onAvatarChange($event)" (avatarDelete)="onAvatarDelete()"></my-actor-avatar-edit> |
7 | </div> | 7 | </div> |
8 | </div> | 8 | </div> |
9 | 9 | ||
diff --git a/client/src/app/+my-account/my-account.module.ts b/client/src/app/+my-account/my-account.module.ts index 076864563..3df48d0aa 100644 --- a/client/src/app/+my-account/my-account.module.ts +++ b/client/src/app/+my-account/my-account.module.ts | |||
@@ -3,6 +3,7 @@ import { TableModule } from 'primeng/table' | |||
3 | import { DragDropModule } from '@angular/cdk/drag-drop' | 3 | import { DragDropModule } from '@angular/cdk/drag-drop' |
4 | import { NgModule } from '@angular/core' | 4 | import { NgModule } from '@angular/core' |
5 | import { SharedAbuseListModule } from '@app/shared/shared-abuse-list' | 5 | import { SharedAbuseListModule } from '@app/shared/shared-abuse-list' |
6 | import { SharedActorImageModule } from '@app/shared/shared-actor-image' | ||
6 | import { SharedFormModule } from '@app/shared/shared-forms' | 7 | import { SharedFormModule } from '@app/shared/shared-forms' |
7 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' | 8 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' |
8 | import { SharedMainModule } from '@app/shared/shared-main' | 9 | import { SharedMainModule } from '@app/shared/shared-main' |
@@ -10,6 +11,7 @@ import { SharedModerationModule } from '@app/shared/shared-moderation' | |||
10 | import { SharedShareModal } from '@app/shared/shared-share-modal' | 11 | import { SharedShareModal } from '@app/shared/shared-share-modal' |
11 | import { SharedUserInterfaceSettingsModule } from '@app/shared/shared-user-settings' | 12 | import { SharedUserInterfaceSettingsModule } from '@app/shared/shared-user-settings' |
12 | import { MyAccountAbusesListComponent } from './my-account-abuses/my-account-abuses-list.component' | 13 | import { MyAccountAbusesListComponent } from './my-account-abuses/my-account-abuses-list.component' |
14 | import { MyAccountApplicationsComponent } from './my-account-applications/my-account-applications.component' | ||
13 | import { MyAccountBlocklistComponent } from './my-account-blocklist/my-account-blocklist.component' | 15 | import { MyAccountBlocklistComponent } from './my-account-blocklist/my-account-blocklist.component' |
14 | import { MyAccountServerBlocklistComponent } from './my-account-blocklist/my-account-server-blocklist.component' | 16 | import { MyAccountServerBlocklistComponent } from './my-account-blocklist/my-account-server-blocklist.component' |
15 | import { MyAccountNotificationsComponent } from './my-account-notifications/my-account-notifications.component' | 17 | import { MyAccountNotificationsComponent } from './my-account-notifications/my-account-notifications.component' |
@@ -20,7 +22,6 @@ import { MyAccountDangerZoneComponent } from './my-account-settings/my-account-d | |||
20 | import { MyAccountNotificationPreferencesComponent } from './my-account-settings/my-account-notification-preferences' | 22 | import { MyAccountNotificationPreferencesComponent } from './my-account-settings/my-account-notification-preferences' |
21 | import { MyAccountProfileComponent } from './my-account-settings/my-account-profile/my-account-profile.component' | 23 | import { MyAccountProfileComponent } from './my-account-settings/my-account-profile/my-account-profile.component' |
22 | import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component' | 24 | import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component' |
23 | import { MyAccountApplicationsComponent } from './my-account-applications/my-account-applications.component' | ||
24 | import { MyAccountComponent } from './my-account.component' | 25 | import { MyAccountComponent } from './my-account.component' |
25 | 26 | ||
26 | @NgModule({ | 27 | @NgModule({ |
@@ -37,7 +38,8 @@ import { MyAccountComponent } from './my-account.component' | |||
37 | SharedUserInterfaceSettingsModule, | 38 | SharedUserInterfaceSettingsModule, |
38 | SharedGlobalIconModule, | 39 | SharedGlobalIconModule, |
39 | SharedAbuseListModule, | 40 | SharedAbuseListModule, |
40 | SharedShareModal | 41 | SharedShareModal, |
42 | SharedActorImageModule | ||
41 | ], | 43 | ], |
42 | 44 | ||
43 | declarations: [ | 45 | declarations: [ |
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts index a625493de..b3265210f 100644 --- a/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts +++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts | |||
@@ -8,10 +8,12 @@ import { | |||
8 | VIDEO_CHANNEL_SUPPORT_VALIDATOR | 8 | VIDEO_CHANNEL_SUPPORT_VALIDATOR |
9 | } from '@app/shared/form-validators/video-channel-validators' | 9 | } from '@app/shared/form-validators/video-channel-validators' |
10 | import { FormValidatorService } from '@app/shared/shared-forms' | 10 | import { FormValidatorService } from '@app/shared/shared-forms' |
11 | import { VideoChannelService } from '@app/shared/shared-main' | 11 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' |
12 | import { VideoChannelCreate } from '@shared/models' | 12 | import { VideoChannelCreate } from '@shared/models' |
13 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | 13 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' |
14 | import { MyVideoChannelEdit } from './my-video-channel-edit' | 14 | import { MyVideoChannelEdit } from './my-video-channel-edit' |
15 | import { switchMap } from 'rxjs/operators' | ||
16 | import { of } from 'rxjs' | ||
15 | 17 | ||
16 | @Component({ | 18 | @Component({ |
17 | templateUrl: './my-video-channel-edit.component.html', | 19 | templateUrl: './my-video-channel-edit.component.html', |
@@ -19,6 +21,10 @@ import { MyVideoChannelEdit } from './my-video-channel-edit' | |||
19 | }) | 21 | }) |
20 | export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements OnInit { | 22 | export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements OnInit { |
21 | error: string | 23 | error: string |
24 | videoChannel = new VideoChannel({}) | ||
25 | |||
26 | private avatar: FormData | ||
27 | private banner: FormData | ||
22 | 28 | ||
23 | constructor ( | 29 | constructor ( |
24 | protected formValidatorService: FormValidatorService, | 30 | protected formValidatorService: FormValidatorService, |
@@ -50,23 +56,43 @@ export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements | |||
50 | support: body.support || null | 56 | support: body.support || null |
51 | } | 57 | } |
52 | 58 | ||
53 | this.videoChannelService.createVideoChannel(videoChannelCreate).subscribe( | 59 | this.videoChannelService.createVideoChannel(videoChannelCreate) |
54 | () => { | 60 | .pipe( |
55 | this.authService.refreshUserInformation() | 61 | switchMap(() => this.uploadAvatar()), |
62 | switchMap(() => this.uploadBanner()) | ||
63 | ).subscribe( | ||
64 | () => { | ||
65 | this.authService.refreshUserInformation() | ||
66 | |||
67 | this.notifier.success($localize`Video channel ${videoChannelCreate.displayName} created.`) | ||
68 | this.router.navigate(['/my-library', 'video-channels']) | ||
69 | }, | ||
56 | 70 | ||
57 | this.notifier.success($localize`Video channel ${videoChannelCreate.displayName} created.`) | 71 | err => { |
58 | this.router.navigate([ '/my-library', 'video-channels' ]) | 72 | if (err.status === HttpStatusCode.CONFLICT_409) { |
59 | }, | 73 | this.error = $localize`This name already exists on this instance.` |
74 | return | ||
75 | } | ||
60 | 76 | ||
61 | err => { | 77 | this.error = err.message |
62 | if (err.status === HttpStatusCode.CONFLICT_409) { | ||
63 | this.error = $localize`This name already exists on this instance.` | ||
64 | return | ||
65 | } | 78 | } |
79 | ) | ||
80 | } | ||
81 | |||
82 | onAvatarChange (formData: FormData) { | ||
83 | this.avatar = formData | ||
84 | } | ||
85 | |||
86 | onAvatarDelete () { | ||
87 | this.avatar = null | ||
88 | } | ||
89 | |||
90 | onBannerChange (formData: FormData) { | ||
91 | this.banner = formData | ||
92 | } | ||
66 | 93 | ||
67 | this.error = err.message | 94 | onBannerDelete () { |
68 | } | 95 | this.banner = null |
69 | ) | ||
70 | } | 96 | } |
71 | 97 | ||
72 | isCreation () { | 98 | isCreation () { |
@@ -76,4 +102,20 @@ export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements | |||
76 | getFormButtonTitle () { | 102 | getFormButtonTitle () { |
77 | return $localize`Create` | 103 | return $localize`Create` |
78 | } | 104 | } |
105 | |||
106 | getUsername () { | ||
107 | return this.form.value.name | ||
108 | } | ||
109 | |||
110 | private uploadAvatar () { | ||
111 | if (!this.avatar) return of(undefined) | ||
112 | |||
113 | return this.videoChannelService.changeVideoChannelImage(this.getUsername(), this.avatar, 'avatar') | ||
114 | } | ||
115 | |||
116 | private uploadBanner () { | ||
117 | if (!this.banner) return of(undefined) | ||
118 | |||
119 | return this.videoChannelService.changeVideoChannelImage(this.getUsername(), this.banner, 'banner') | ||
120 | } | ||
79 | } | 121 | } |
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html index 735f9e3ba..2910dffad 100644 --- a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html +++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html | |||
@@ -10,7 +10,7 @@ | |||
10 | <ng-container *ngIf="!isCreation()"> | 10 | <ng-container *ngIf="!isCreation()"> |
11 | <li class="breadcrumb-item active" i18n>Edit</li> | 11 | <li class="breadcrumb-item active" i18n>Edit</li> |
12 | <li class="breadcrumb-item active" aria-current="page"> | 12 | <li class="breadcrumb-item active" aria-current="page"> |
13 | <a *ngIf="videoChannelToUpdate" [routerLink]="[ '/my-library/video-channels/update', videoChannelToUpdate?.nameWithHost ]">{{ videoChannelToUpdate?.displayName }}</a> | 13 | <a *ngIf="videoChannel" [routerLink]="[ '/my-library/video-channels/update', videoChannel?.nameWithHost ]">{{ videoChannel?.displayName }}</a> |
14 | </li> | 14 | </li> |
15 | </ng-container> | 15 | </ng-container> |
16 | </ol> | 16 | </ol> |
@@ -23,10 +23,22 @@ | |||
23 | <div class="form-row"> <!-- channel grid --> | 23 | <div class="form-row"> <!-- channel grid --> |
24 | <div class="form-group col-12 col-lg-4 col-xl-3"> | 24 | <div class="form-group col-12 col-lg-4 col-xl-3"> |
25 | <div *ngIf="isCreation()" class="video-channel-title" i18n>NEW CHANNEL</div> | 25 | <div *ngIf="isCreation()" class="video-channel-title" i18n>NEW CHANNEL</div> |
26 | <div *ngIf="!isCreation() && videoChannelToUpdate" class="video-channel-title" i18n>CHANNEL</div> | 26 | <div *ngIf="!isCreation() && videoChannel" class="video-channel-title" i18n>CHANNEL</div> |
27 | </div> | 27 | </div> |
28 | 28 | ||
29 | <div class="form-group col-12 col-lg-8 col-xl-9"> | 29 | <div class="form-group col-12 col-lg-8 col-xl-9"> |
30 | <h6 i18n>Banner image of your channel</h6> | ||
31 | |||
32 | <my-actor-banner-edit | ||
33 | *ngIf="videoChannel" [previewImage]="isCreation()" | ||
34 | [actor]="videoChannel" (bannerChange)="onBannerChange($event)" (bannerDelete)="onBannerDelete()" | ||
35 | ></my-actor-banner-edit> | ||
36 | |||
37 | <my-actor-avatar-edit | ||
38 | *ngIf="videoChannel" [previewImage]="isCreation()" | ||
39 | [actor]="videoChannel" (avatarChange)="onAvatarChange($event)" (avatarDelete)="onAvatarDelete()" | ||
40 | [displayUsername]="!isCreation()" [displaySubscribers]="!isCreation()" | ||
41 | ></my-actor-avatar-edit> | ||
30 | 42 | ||
31 | <div class="form-group" *ngIf="isCreation()"> | 43 | <div class="form-group" *ngIf="isCreation()"> |
32 | <label i18n for="name">Name</label> | 44 | <label i18n for="name">Name</label> |
@@ -44,11 +56,6 @@ | |||
44 | </div> | 56 | </div> |
45 | </div> | 57 | </div> |
46 | 58 | ||
47 | <my-actor-avatar-info | ||
48 | *ngIf="!isCreation() && videoChannelToUpdate" | ||
49 | [actor]="videoChannelToUpdate" (avatarChange)="onAvatarChange($event)" (avatarDelete)="onAvatarDelete()" | ||
50 | ></my-actor-avatar-info> | ||
51 | |||
52 | <div class="form-group"> | 59 | <div class="form-group"> |
53 | <label i18n for="display-name">Display name</label> | 60 | <label i18n for="display-name">Display name</label> |
54 | <input | 61 | <input |
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss index 8f8af655c..22de103d1 100644 --- a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss +++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss | |||
@@ -10,11 +10,16 @@ label { | |||
10 | @include settings-big-title; | 10 | @include settings-big-title; |
11 | } | 11 | } |
12 | 12 | ||
13 | my-actor-avatar-info { | 13 | my-actor-avatar-edit, |
14 | my-actor-banner-edit { | ||
14 | display: block; | 15 | display: block; |
15 | margin-bottom: 20px; | 16 | margin-bottom: 20px; |
16 | } | 17 | } |
17 | 18 | ||
19 | my-actor-banner-edit { | ||
20 | max-width: 500px; | ||
21 | } | ||
22 | |||
18 | .input-group { | 23 | .input-group { |
19 | @include peertube-input-group(fit-content); | 24 | @include peertube-input-group(fit-content); |
20 | } | 25 | } |
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.ts b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.ts index 3e20a27ee..33bb90f14 100644 --- a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.ts +++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.ts | |||
@@ -2,8 +2,7 @@ import { FormReactive } from '@app/shared/shared-forms' | |||
2 | import { VideoChannel } from '@app/shared/shared-main' | 2 | import { VideoChannel } from '@app/shared/shared-main' |
3 | 3 | ||
4 | export abstract class MyVideoChannelEdit extends FormReactive { | 4 | export abstract class MyVideoChannelEdit extends FormReactive { |
5 | // We need it even in the create component because it's used in the edit template | 5 | videoChannel: VideoChannel |
6 | videoChannelToUpdate: VideoChannel | ||
7 | 6 | ||
8 | abstract isCreation (): boolean | 7 | abstract isCreation (): boolean |
9 | abstract getFormButtonTitle (): string | 8 | abstract getFormButtonTitle (): string |
@@ -12,10 +11,6 @@ export abstract class MyVideoChannelEdit extends FormReactive { | |||
12 | return window.location.host | 11 | return window.location.host |
13 | } | 12 | } |
14 | 13 | ||
15 | // We need this method so angular does not complain in child template that doesn't need this | ||
16 | onAvatarChange (formData: FormData) { /* empty */ } | ||
17 | onAvatarDelete () { /* empty */ } | ||
18 | |||
19 | // Should be implemented by the child | 14 | // Should be implemented by the child |
20 | isBulkUpdateVideosDisplayed () { | 15 | isBulkUpdateVideosDisplayed () { |
21 | return false | 16 | return false |
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts index 6cd1ff503..a29af176c 100644 --- a/client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts +++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts | |||
@@ -1,7 +1,9 @@ | |||
1 | import { Subscription } from 'rxjs' | 1 | import { Subscription } from 'rxjs' |
2 | import { HttpErrorResponse } from '@angular/common/http' | ||
2 | import { Component, OnDestroy, OnInit } from '@angular/core' | 3 | import { Component, OnDestroy, OnInit } from '@angular/core' |
3 | import { ActivatedRoute, Router } from '@angular/router' | 4 | import { ActivatedRoute, Router } from '@angular/router' |
4 | import { AuthService, Notifier, ServerService } from '@app/core' | 5 | import { AuthService, Notifier, ServerService } from '@app/core' |
6 | import { uploadErrorHandler } from '@app/helpers' | ||
5 | import { | 7 | import { |
6 | VIDEO_CHANNEL_DESCRIPTION_VALIDATOR, | 8 | VIDEO_CHANNEL_DESCRIPTION_VALIDATOR, |
7 | VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR, | 9 | VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR, |
@@ -11,8 +13,6 @@ import { FormValidatorService } from '@app/shared/shared-forms' | |||
11 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' | 13 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' |
12 | import { ServerConfig, VideoChannelUpdate } from '@shared/models' | 14 | import { ServerConfig, VideoChannelUpdate } from '@shared/models' |
13 | import { MyVideoChannelEdit } from './my-video-channel-edit' | 15 | import { MyVideoChannelEdit } from './my-video-channel-edit' |
14 | import { HttpErrorResponse } from '@angular/common/http' | ||
15 | import { uploadErrorHandler } from '@app/helpers' | ||
16 | 16 | ||
17 | @Component({ | 17 | @Component({ |
18 | selector: 'my-video-channel-update', | 18 | selector: 'my-video-channel-update', |
@@ -21,7 +21,7 @@ import { uploadErrorHandler } from '@app/helpers' | |||
21 | }) | 21 | }) |
22 | export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements OnInit, OnDestroy { | 22 | export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements OnInit, OnDestroy { |
23 | error: string | 23 | error: string |
24 | videoChannelToUpdate: VideoChannel | 24 | videoChannel: VideoChannel |
25 | 25 | ||
26 | private paramsSub: Subscription | 26 | private paramsSub: Subscription |
27 | private oldSupportField: string | 27 | private oldSupportField: string |
@@ -56,7 +56,7 @@ export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements | |||
56 | 56 | ||
57 | this.videoChannelService.getVideoChannel(videoChannelId).subscribe( | 57 | this.videoChannelService.getVideoChannel(videoChannelId).subscribe( |
58 | videoChannelToUpdate => { | 58 | videoChannelToUpdate => { |
59 | this.videoChannelToUpdate = videoChannelToUpdate | 59 | this.videoChannel = videoChannelToUpdate |
60 | 60 | ||
61 | this.oldSupportField = videoChannelToUpdate.support | 61 | this.oldSupportField = videoChannelToUpdate.support |
62 | 62 | ||
@@ -87,7 +87,7 @@ export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements | |||
87 | bulkVideosSupportUpdate: body.bulkVideosSupportUpdate || false | 87 | bulkVideosSupportUpdate: body.bulkVideosSupportUpdate || false |
88 | } | 88 | } |
89 | 89 | ||
90 | this.videoChannelService.updateVideoChannel(this.videoChannelToUpdate.name, videoChannelUpdate).subscribe( | 90 | this.videoChannelService.updateVideoChannel(this.videoChannel.name, videoChannelUpdate).subscribe( |
91 | () => { | 91 | () => { |
92 | this.authService.refreshUserInformation() | 92 | this.authService.refreshUserInformation() |
93 | 93 | ||
@@ -101,12 +101,12 @@ export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements | |||
101 | } | 101 | } |
102 | 102 | ||
103 | onAvatarChange (formData: FormData) { | 103 | onAvatarChange (formData: FormData) { |
104 | this.videoChannelService.changeVideoChannelAvatar(this.videoChannelToUpdate.name, formData) | 104 | this.videoChannelService.changeVideoChannelImage(this.videoChannel.name, formData, 'avatar') |
105 | .subscribe( | 105 | .subscribe( |
106 | data => { | 106 | data => { |
107 | this.notifier.success($localize`Avatar changed.`) | 107 | this.notifier.success($localize`Avatar changed.`) |
108 | 108 | ||
109 | this.videoChannelToUpdate.updateAvatar(data.avatar) | 109 | this.videoChannel.updateAvatar(data.avatar) |
110 | }, | 110 | }, |
111 | 111 | ||
112 | (err: HttpErrorResponse) => uploadErrorHandler({ | 112 | (err: HttpErrorResponse) => uploadErrorHandler({ |
@@ -118,12 +118,42 @@ export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements | |||
118 | } | 118 | } |
119 | 119 | ||
120 | onAvatarDelete () { | 120 | onAvatarDelete () { |
121 | this.videoChannelService.deleteVideoChannelAvatar(this.videoChannelToUpdate.name) | 121 | this.videoChannelService.deleteVideoChannelImage(this.videoChannel.name, 'avatar') |
122 | .subscribe( | 122 | .subscribe( |
123 | data => { | 123 | data => { |
124 | this.notifier.success($localize`Avatar deleted.`) | 124 | this.notifier.success($localize`Avatar deleted.`) |
125 | 125 | ||
126 | this.videoChannelToUpdate.resetAvatar() | 126 | this.videoChannel.resetAvatar() |
127 | }, | ||
128 | |||
129 | err => this.notifier.error(err.message) | ||
130 | ) | ||
131 | } | ||
132 | |||
133 | onBannerChange (formData: FormData) { | ||
134 | this.videoChannelService.changeVideoChannelImage(this.videoChannel.name, formData, 'banner') | ||
135 | .subscribe( | ||
136 | data => { | ||
137 | this.notifier.success($localize`Banner changed.`) | ||
138 | |||
139 | this.videoChannel.updateBanner(data.banner) | ||
140 | }, | ||
141 | |||
142 | (err: HttpErrorResponse) => uploadErrorHandler({ | ||
143 | err, | ||
144 | name: $localize`banner`, | ||
145 | notifier: this.notifier | ||
146 | }) | ||
147 | ) | ||
148 | } | ||
149 | |||
150 | onBannerDelete () { | ||
151 | this.videoChannelService.deleteVideoChannelImage(this.videoChannel.name, 'banner') | ||
152 | .subscribe( | ||
153 | data => { | ||
154 | this.notifier.success($localize`Banner deleted.`) | ||
155 | |||
156 | this.videoChannel.resetBanner() | ||
127 | }, | 157 | }, |
128 | 158 | ||
129 | err => this.notifier.error(err.message) | 159 | err => this.notifier.error(err.message) |
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss index f2f42459f..8804fa95c 100644 --- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss +++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss | |||
@@ -17,10 +17,11 @@ input[type=text] { | |||
17 | 17 | ||
18 | .video-channel { | 18 | .video-channel { |
19 | @include row-blocks; | 19 | @include row-blocks; |
20 | |||
20 | padding-bottom: 0; | 21 | padding-bottom: 0; |
21 | 22 | ||
22 | img { | 23 | img { |
23 | @include avatar(80px); | 24 | @include channel-avatar(80px); |
24 | 25 | ||
25 | margin-right: 10px; | 26 | margin-right: 10px; |
26 | } | 27 | } |
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts b/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts index 92b56db49..53557ca02 100644 --- a/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts +++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { ChartModule } from 'primeng/chart' | 1 | import { ChartModule } from 'primeng/chart' |
2 | import { NgModule } from '@angular/core' | 2 | import { NgModule } from '@angular/core' |
3 | import { SharedActorImageModule } from '@app/shared/shared-actor-image' | ||
3 | import { SharedFormModule } from '@app/shared/shared-forms' | 4 | import { SharedFormModule } from '@app/shared/shared-forms' |
4 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' | 5 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' |
5 | import { SharedMainModule } from '@app/shared/shared-main' | 6 | import { SharedMainModule } from '@app/shared/shared-main' |
@@ -16,7 +17,8 @@ import { MyVideoChannelsComponent } from './my-video-channels.component' | |||
16 | 17 | ||
17 | SharedMainModule, | 18 | SharedMainModule, |
18 | SharedFormModule, | 19 | SharedFormModule, |
19 | SharedGlobalIconModule | 20 | SharedGlobalIconModule, |
21 | SharedActorImageModule | ||
20 | ], | 22 | ], |
21 | 23 | ||
22 | declarations: [ | 24 | declarations: [ |
diff --git a/client/src/app/+my-library/my-history/my-history.component.html b/client/src/app/+my-library/my-history/my-history.component.html index c180161e7..9dec64645 100644 --- a/client/src/app/+my-library/my-history/my-history.component.html +++ b/client/src/app/+my-library/my-history/my-history.component.html | |||
@@ -4,7 +4,7 @@ | |||
4 | </h1> | 4 | </h1> |
5 | 5 | ||
6 | <div class="top-buttons"> | 6 | <div class="top-buttons"> |
7 | <div> | 7 | <div class="search-wrapper"> |
8 | <div class="input-group has-feedback has-clear"> | 8 | <div class="input-group has-feedback has-clear"> |
9 | <input | 9 | <input |
10 | type="text" name="history-search" id="history-search" i18n-placeholder placeholder="Search your history" | 10 | type="text" name="history-search" id="history-search" i18n-placeholder placeholder="Search your history" |
@@ -15,7 +15,7 @@ | |||
15 | </div> | 15 | </div> |
16 | </div> | 16 | </div> |
17 | 17 | ||
18 | <div class="history-switch ml-auto mr-3"> | 18 | <div class="history-switch"> |
19 | <my-input-switch [(ngModel)]="videosHistoryEnabled" (ngModelChange)="onVideosHistoryChange()"></my-input-switch> | 19 | <my-input-switch [(ngModel)]="videosHistoryEnabled" (ngModelChange)="onVideosHistoryChange()"></my-input-switch> |
20 | <label i18n>Track watch history</label> | 20 | <label i18n>Track watch history</label> |
21 | </div> | 21 | </div> |
diff --git a/client/src/app/+my-library/my-history/my-history.component.scss b/client/src/app/+my-library/my-history/my-history.component.scss index 928a8a3da..af4a34b4b 100644 --- a/client/src/app/+my-library/my-history/my-history.component.scss +++ b/client/src/app/+my-library/my-history/my-history.component.scss | |||
@@ -11,16 +11,24 @@ | |||
11 | 11 | ||
12 | .top-buttons { | 12 | .top-buttons { |
13 | margin-bottom: 30px; | 13 | margin-bottom: 30px; |
14 | display: flex; | 14 | display: grid; |
15 | grid-template-columns: 250px 1fr auto auto; | ||
15 | align-items: center; | 16 | align-items: center; |
16 | flex-wrap: wrap; | ||
17 | 17 | ||
18 | #history-search { | 18 | .search-wrapper { |
19 | @include peertube-input-text(250px); | 19 | grid-column: 1; |
20 | |||
21 | input { | ||
22 | @include peertube-input-text(250px); | ||
23 | } | ||
20 | } | 24 | } |
21 | 25 | ||
22 | .history-switch { | 26 | .history-switch { |
27 | grid-column: 3; | ||
28 | |||
23 | display: flex; | 29 | display: flex; |
30 | margin-left: auto; | ||
31 | margin-right: 15px; | ||
24 | 32 | ||
25 | label { | 33 | label { |
26 | margin: 0 0 0 5px; | 34 | margin: 0 0 0 5px; |
@@ -31,6 +39,8 @@ | |||
31 | } | 39 | } |
32 | 40 | ||
33 | .delete-history { | 41 | .delete-history { |
42 | grid-column: 4; | ||
43 | |||
34 | @include peertube-button; | 44 | @include peertube-button; |
35 | @include grey-button; | 45 | @include grey-button; |
36 | @include button-with-icon; | 46 | @include button-with-icon; |
@@ -40,26 +50,27 @@ | |||
40 | } | 50 | } |
41 | 51 | ||
42 | .video { | 52 | .video { |
43 | @include row-blocks; | 53 | @include row-blocks($column-responsive: false); |
44 | |||
45 | .my-video-miniature { | ||
46 | flex-grow: 1; | ||
47 | } | ||
48 | } | 54 | } |
49 | 55 | ||
50 | @media screen and (max-width: $mobile-view) { | 56 | @media screen and (max-width: $small-view) { |
51 | .top-buttons { | 57 | .top-buttons { |
52 | .history-switch label, .delete-history { | 58 | grid-template-columns: auto 1fr auto; |
53 | @include ellipsis; | 59 | row-gap: 20px; |
54 | } | ||
55 | 60 | ||
56 | .history-switch label { | 61 | .history-switch { |
57 | width: 60%; | 62 | grid-row: 1; |
63 | grid-column: 1; | ||
64 | margin: 0; | ||
58 | } | 65 | } |
59 | 66 | ||
60 | .delete-history { | 67 | .delete-history { |
61 | margin-left: auto; | 68 | grid-row: 1; |
62 | max-width: 32%; | 69 | grid-column: 3; |
70 | } | ||
71 | |||
72 | .search-wrapper { | ||
73 | grid-column: 1 / 4; | ||
63 | } | 74 | } |
64 | } | 75 | } |
65 | } | 76 | } |
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html index 510b400c0..ff448ad87 100644 --- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html +++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html | |||
@@ -6,7 +6,7 @@ | |||
6 | </span> | 6 | </span> |
7 | </h1> | 7 | </h1> |
8 | 8 | ||
9 | <div class="video-subscriptions-header d-flex justify-content-between"> | 9 | <div class="video-subscriptions-header"> |
10 | <div class="has-feedback has-clear"> | 10 | <div class="has-feedback has-clear"> |
11 | <input type="text" placeholder="Search your subscriptions" i18n-placeholder [(ngModel)]="subscriptionsSearch" | 11 | <input type="text" placeholder="Search your subscriptions" i18n-placeholder [(ngModel)]="subscriptionsSearch" |
12 | (ngModelChange)="onSubscriptionsSearchChanged()" /> | 12 | (ngModelChange)="onSubscriptionsSearchChanged()" /> |
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss index 5ead45dd8..3c1a4d2ad 100644 --- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss +++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss | |||
@@ -9,40 +9,40 @@ input[type=text] { | |||
9 | @include row-blocks; | 9 | @include row-blocks; |
10 | 10 | ||
11 | img { | 11 | img { |
12 | @include avatar(80px); | 12 | @include channel-avatar(80px); |
13 | 13 | ||
14 | margin-right: 10px; | 14 | margin-right: 10px; |
15 | } | 15 | } |
16 | } | ||
16 | 17 | ||
17 | .video-channel-info { | 18 | .video-channel-info { |
18 | flex-grow: 1; | 19 | flex-grow: 1; |
19 | 20 | ||
20 | a.video-channel-names { | 21 | a.video-channel-names { |
21 | @include disable-default-a-behaviour; | 22 | @include disable-default-a-behaviour; |
22 | 23 | ||
23 | width: fit-content; | 24 | width: fit-content; |
24 | display: flex; | 25 | display: flex; |
25 | align-items: baseline; | 26 | align-items: baseline; |
26 | color: pvar(--mainForegroundColor); | 27 | color: pvar(--mainForegroundColor); |
27 | 28 | ||
28 | .video-channel-display-name { | 29 | .video-channel-display-name { |
29 | font-weight: $font-semibold; | 30 | font-weight: $font-semibold; |
30 | font-size: 18px; | 31 | font-size: 18px; |
31 | } | 32 | } |
32 | 33 | ||
33 | .video-channel-name { | 34 | .video-channel-name { |
34 | font-size: 14px; | 35 | font-size: 14px; |
35 | color: $grey-actor-name; | 36 | color: $grey-actor-name; |
36 | margin-left: 5px; | 37 | margin-left: 5px; |
37 | } | ||
38 | } | 38 | } |
39 | } | 39 | } |
40 | } | ||
40 | 41 | ||
41 | .actor-owner { | 42 | .actor-owner { |
42 | @include actor-owner; | 43 | @include actor-owner; |
43 | 44 | ||
44 | margin-top: 0; | 45 | margin-top: 0; |
45 | } | ||
46 | } | 46 | } |
47 | 47 | ||
48 | .video-subscriptions-header { | 48 | .video-subscriptions-header { |
@@ -50,32 +50,22 @@ input[type=text] { | |||
50 | } | 50 | } |
51 | 51 | ||
52 | @media screen and (max-width: $small-view) { | 52 | @media screen and (max-width: $small-view) { |
53 | .video-channel { | 53 | .video-subscriptions-header input[type=text] { |
54 | .video-channel-info { | 54 | width: 100% !important; |
55 | padding-bottom: 10px; | ||
56 | text-align: center; | ||
57 | |||
58 | .video-channel-names { | ||
59 | flex-direction: column; | ||
60 | align-items: center !important; | ||
61 | margin: auto; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | img { | ||
66 | margin-right: 0; | ||
67 | } | ||
68 | } | 55 | } |
69 | } | ||
70 | 56 | ||
71 | @media screen and (max-width: $mobile-view) { | 57 | .video-channel-info { |
72 | .video-subscriptions-header { | 58 | padding-bottom: 10px; |
73 | flex-direction: column; | 59 | text-align: center; |
74 | 60 | ||
75 | input[type=text] { | 61 | .video-channel-names { |
76 | width: 100% !important; | 62 | flex-direction: column; |
63 | align-items: center !important; | ||
64 | margin: auto; | ||
77 | } | 65 | } |
78 | } | 66 | } |
79 | } | ||
80 | |||
81 | 67 | ||
68 | img { | ||
69 | margin-right: 0; | ||
70 | } | ||
71 | } | ||
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.html b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.html index a97b2b4fb..e7e3c17b3 100644 --- a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.html +++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.html | |||
@@ -1,6 +1,6 @@ | |||
1 | <div class="row"> | 1 | <div class="root"> |
2 | 2 | ||
3 | <div class="playlist-info col-xs-12 col-md-5 col-xl-3"> | 3 | <div class="playlist-info"> |
4 | <my-video-playlist-miniature | 4 | <my-video-playlist-miniature |
5 | *ngIf="playlist" [playlist]="playlist" [toManage]="false" [displayChannel]="true" | 5 | *ngIf="playlist" [playlist]="playlist" [toManage]="false" [displayChannel]="true" |
6 | [displayDescription]="true" [displayPrivacy]="true" | 6 | [displayDescription]="true" [displayPrivacy]="true" |
@@ -20,7 +20,7 @@ | |||
20 | 20 | ||
21 | </div> | 21 | </div> |
22 | 22 | ||
23 | <div class="playlist-elements col-xs-12 col-md-7 col-xl-9"> | 23 | <div class="playlist-elements"> |
24 | <div class="no-results" *ngIf="pagination.totalItems === 0"> | 24 | <div class="no-results" *ngIf="pagination.totalItems === 0"> |
25 | <div i18n>No videos in this playlist.</div> | 25 | <div i18n>No videos in this playlist.</div> |
26 | 26 | ||
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss index de7e1993f..0c68dedf6 100644 --- a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss +++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss | |||
@@ -2,21 +2,25 @@ | |||
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | @import '_miniature'; | 3 | @import '_miniature'; |
4 | 4 | ||
5 | .root { | ||
6 | display: grid; | ||
7 | grid-template-columns: auto 1fr; | ||
8 | } | ||
9 | |||
5 | .playlist-info { | 10 | .playlist-info { |
6 | background-color: pvar(--submenuColor); | 11 | grid-column: 1; |
7 | margin-left: -$not-expanded-horizontal-margins; | 12 | background-color: pvar(--submenuBackgroundColor); |
13 | margin-left: calc(#{pvar(--horizontalMarginContent)} * -1); | ||
8 | margin-top: -$sub-menu-margin-bottom; | 14 | margin-top: -$sub-menu-margin-bottom; |
9 | 15 | ||
10 | padding: 10px; | 16 | padding: 15px; |
11 | 17 | ||
12 | display: flex; | 18 | display: flex; |
13 | flex-direction: column; | 19 | flex-direction: column; |
14 | justify-content: flex-start; | ||
15 | align-items: center; | ||
16 | 20 | ||
17 | /* fix ellipsis dots background color */ | 21 | /* fix ellipsis dots background color */ |
18 | ::ng-deep .miniature-name::after { | 22 | ::ng-deep .miniature-name::after { |
19 | background-color: pvar(--submenuColor) !important; | 23 | background-color: pvar(--submenuBackgroundColor) !important; |
20 | } | 24 | } |
21 | } | 25 | } |
22 | 26 | ||
@@ -59,15 +63,35 @@ | |||
59 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); | 63 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); |
60 | } | 64 | } |
61 | 65 | ||
62 | @media screen and (max-width: $small-view) { | 66 | .playlist-elements { |
67 | grid-column: 2; | ||
68 | } | ||
69 | |||
70 | my-video-playlist-miniature { | ||
71 | width: $video-thumbnail-width; | ||
72 | } | ||
73 | |||
74 | @include on-small-main-col { | ||
75 | my-video-playlist-miniature { | ||
76 | width: $video-thumbnail-medium-width; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | @include on-mobile-main-col { | ||
81 | .root { | ||
82 | display: block; | ||
83 | } | ||
84 | |||
63 | .playlist-info { | 85 | .playlist-info { |
64 | width: 100vw; | 86 | width: calc(100% + (2 * var(--horizontalMarginContent))); |
65 | padding-top: 20px; | 87 | padding-top: 20px; |
66 | margin-left: calc(#{var(--expanded-horizontal-margin-content)} * -1); | 88 | margin-bottom: 10px; |
67 | } | 89 | } |
68 | 90 | ||
69 | .playlist-elements { | 91 | my-video-playlist-miniature, |
70 | padding: 0 !important; | 92 | .playlist-buttons { |
93 | margin-left: auto; | ||
94 | margin-right: auto; | ||
71 | } | 95 | } |
72 | 96 | ||
73 | ::ng-deep my-video-playlist-element-miniature { | 97 | ::ng-deep my-video-playlist-element-miniature { |
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html index afcf6a084..b88ea3db7 100644 --- a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html +++ b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html | |||
@@ -1,8 +1,6 @@ | |||
1 | <h1> | 1 | <h1> |
2 | <span> | 2 | <my-global-icon iconName="playlists" aria-hidden="true"></my-global-icon> |
3 | <my-global-icon iconName="playlists" aria-hidden="true"></my-global-icon> | 3 | <ng-container i18n>My playlists</ng-container> <span class="badge badge-secondary">{{ pagination.totalItems }}</span> |
4 | <ng-container i18n>My playlists</ng-container> <span class="badge badge-secondary">{{ pagination.totalItems }}</span> | ||
5 | </span> | ||
6 | </h1> | 4 | </h1> |
7 | 5 | ||
8 | <div class="video-playlists-header d-flex justify-content-between"> | 6 | <div class="video-playlists-header d-flex justify-content-between"> |
@@ -21,10 +19,10 @@ | |||
21 | 19 | ||
22 | <div class="video-playlists" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()"> | 20 | <div class="video-playlists" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()"> |
23 | <div *ngFor="let playlist of videoPlaylists" class="video-playlist"> | 21 | <div *ngFor="let playlist of videoPlaylists" class="video-playlist"> |
24 | <div class="miniature-wrapper"> | 22 | <my-video-playlist-miniature |
25 | <my-video-playlist-miniature [playlist]="playlist" [toManage]="true" [displayChannel]="true" [displayDescription]="true" [displayPrivacy]="true" | 23 | [playlist]="playlist" [toManage]="true" [displayChannel]="true" |
26 | ></my-video-playlist-miniature> | 24 | [displayDescription]="true" [displayPrivacy]="true" [displayAsRow]="true" |
27 | </div> | 25 | ></my-video-playlist-miniature> |
28 | 26 | ||
29 | <div *ngIf="isRegularPlaylist(playlist)" class="video-playlist-buttons"> | 27 | <div *ngIf="isRegularPlaylist(playlist)" class="video-playlist-buttons"> |
30 | <my-delete-button label (click)="deleteVideoPlaylist(playlist)"></my-delete-button> | 28 | <my-delete-button label (click)="deleteVideoPlaylist(playlist)"></my-delete-button> |
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss index 2b7c88246..94187efd4 100644 --- a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss +++ b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss | |||
@@ -1,6 +1,10 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | h1 { | ||
5 | display: flex; | ||
6 | } | ||
7 | |||
4 | .create-button { | 8 | .create-button { |
5 | @include create-button; | 9 | @include create-button; |
6 | } | 10 | } |
@@ -9,64 +13,45 @@ input[type=text] { | |||
9 | @include peertube-input-text(300px); | 13 | @include peertube-input-text(300px); |
10 | } | 14 | } |
11 | 15 | ||
12 | ::ng-deep .action-button { | ||
13 | &.action-button-delete { | ||
14 | margin-right: 10px; | ||
15 | } | ||
16 | } | ||
17 | |||
18 | .video-playlist { | 16 | .video-playlist { |
19 | @include row-blocks; | 17 | @include row-blocks($column-responsive: false); |
20 | 18 | } | |
21 | .miniature-wrapper { | ||
22 | flex-grow: 1; | ||
23 | |||
24 | ::ng-deep .miniature { | ||
25 | display: flex; | ||
26 | |||
27 | .miniature-info { | ||
28 | margin-left: 10px; | ||
29 | width: auto; | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | 19 | ||
34 | .video-playlist-buttons { | 20 | .video-playlist-buttons { |
35 | min-width: 190px; | 21 | display: flex; |
36 | height: max-content; | 22 | margin-left: 10px; |
37 | } | 23 | align-self: flex-end; |
38 | } | 24 | } |
39 | 25 | ||
40 | .video-playlists-header { | 26 | .video-playlists-header { |
41 | margin-bottom: 30px; | 27 | margin-bottom: 30px; |
42 | } | 28 | } |
43 | 29 | ||
44 | @media screen and (max-width: $small-view) { | 30 | my-video-playlist-miniature { |
31 | display: block; | ||
32 | flex-grow: 1; | ||
33 | } | ||
34 | |||
35 | my-delete-button { | ||
36 | margin-right: 10px; | ||
37 | } | ||
38 | |||
39 | @include on-small-main-col { | ||
45 | .video-playlists-header { | 40 | .video-playlists-header { |
46 | text-align: center; | 41 | text-align: center; |
47 | } | 42 | } |
48 | 43 | ||
49 | .video-playlist { | 44 | .video-playlist { |
50 | 45 | flex-wrap: wrap; | |
51 | .video-playlist-buttons { | ||
52 | margin-top: 10px; | ||
53 | } | ||
54 | } | 46 | } |
55 | 47 | ||
56 | my-video-playlist-miniature ::ng-deep .miniature { | 48 | .video-playlist-buttons { |
57 | flex-direction: column; | 49 | margin-top: 10px; |
58 | 50 | margin-left: auto; | |
59 | .miniature-info { | ||
60 | margin-left: 0 !important; | ||
61 | } | ||
62 | |||
63 | .miniature-name { | ||
64 | max-width: $video-thumbnail-width; | ||
65 | } | ||
66 | } | 51 | } |
67 | } | 52 | } |
68 | 53 | ||
69 | @media screen and (max-width: $mobile-view) { | 54 | @include on-mobile-main-col { |
70 | .video-playlists-header { | 55 | .video-playlists-header { |
71 | flex-direction: column; | 56 | flex-direction: column; |
72 | 57 | ||
@@ -75,4 +60,8 @@ input[type=text] { | |||
75 | margin-bottom: 12px; | 60 | margin-bottom: 12px; |
76 | } | 61 | } |
77 | } | 62 | } |
63 | |||
64 | .action-button { | ||
65 | margin-left: 0; | ||
66 | } | ||
78 | } | 67 | } |
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.html b/client/src/app/+my-library/my-videos/my-videos.component.html index 5fa4c02ec..e9f436378 100644 --- a/client/src/app/+my-library/my-videos/my-videos.component.html +++ b/client/src/app/+my-library/my-videos/my-videos.component.html | |||
@@ -25,6 +25,17 @@ | |||
25 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> | 25 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> |
26 | <span class="sr-only" i18n>Clear filters</span> | 26 | <span class="sr-only" i18n>Clear filters</span> |
27 | </div> | 27 | </div> |
28 | |||
29 | <div class="peertube-select-container peertube-select-button"> | ||
30 | <select [(ngModel)]="sort" (ngModelChange)="onChangeSortColumn()" class="form-control"> | ||
31 | <option value="undefined" disabled>Sort by</option> | ||
32 | <option value="-publishedAt" i18n>Last published first</option> | ||
33 | <option value="-createdAt" i18n>Last created first</option> | ||
34 | <option value="-views" i18n>Most viewed first</option> | ||
35 | <option value="-likes" i18n>Most liked first</option> | ||
36 | <option value="-duration" i18n>Longest first</option> | ||
37 | </select> | ||
38 | </div> | ||
28 | </div> | 39 | </div> |
29 | 40 | ||
30 | <my-videos-selection | 41 | <my-videos-selection |
@@ -34,7 +45,6 @@ | |||
34 | [miniatureDisplayOptions]="miniatureDisplayOptions" | 45 | [miniatureDisplayOptions]="miniatureDisplayOptions" |
35 | [titlePage]="titlePage" | 46 | [titlePage]="titlePage" |
36 | [getVideosObservableFunction]="getVideosObservableFunction" | 47 | [getVideosObservableFunction]="getVideosObservableFunction" |
37 | [ownerDisplayType]="ownerDisplayType" | ||
38 | [user]="user" | 48 | [user]="user" |
39 | #videosSelection | 49 | #videosSelection |
40 | > | 50 | > |
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.scss b/client/src/app/+my-library/my-videos/my-videos.component.scss index 59fc5fe80..aaf21126b 100644 --- a/client/src/app/+my-library/my-videos/my-videos.component.scss +++ b/client/src/app/+my-library/my-videos/my-videos.component.scss | |||
@@ -5,6 +5,11 @@ input[type=text] { | |||
5 | @include peertube-input-text(300px); | 5 | @include peertube-input-text(300px); |
6 | } | 6 | } |
7 | 7 | ||
8 | .peertube-select-container { | ||
9 | @include peertube-select-container(auto); | ||
10 | margin-left: 0.5rem; | ||
11 | } | ||
12 | |||
8 | h1 { | 13 | h1 { |
9 | display: flex; | 14 | display: flex; |
10 | justify-content: space-between; | 15 | justify-content: space-between; |
@@ -32,36 +37,9 @@ h1 { | |||
32 | } | 37 | } |
33 | } | 38 | } |
34 | 39 | ||
35 | ::ng-deep { | ||
36 | .video { | ||
37 | flex-wrap: wrap; | ||
38 | } | ||
39 | |||
40 | .action-button span { | ||
41 | white-space: nowrap; | ||
42 | } | ||
43 | |||
44 | .video-miniature { | ||
45 | &.display-as-row { | ||
46 | // width: min-content !important; | ||
47 | width: 100% !important; | ||
48 | |||
49 | .video-bottom .video-miniature-information { | ||
50 | width: max-content !important; | ||
51 | min-width: unset !important; | ||
52 | } | ||
53 | } | ||
54 | |||
55 | .video-bottom { | ||
56 | max-width: 350px; | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
61 | .action-button { | 40 | .action-button { |
62 | display: flex; | 41 | display: flex; |
63 | margin-left: 55px; | 42 | margin-left: 10px; |
64 | margin-top: 10px; | ||
65 | align-self: flex-end; | 43 | align-self: flex-end; |
66 | } | 44 | } |
67 | 45 | ||
@@ -69,7 +47,7 @@ my-edit-button { | |||
69 | margin-right: 10px; | 47 | margin-right: 10px; |
70 | } | 48 | } |
71 | 49 | ||
72 | @media screen and (max-width: $small-view) { | 50 | @include on-small-main-col { |
73 | h1 { | 51 | h1 { |
74 | flex-direction: column; | 52 | flex-direction: column; |
75 | 53 | ||
@@ -80,59 +58,25 @@ my-edit-button { | |||
80 | } | 58 | } |
81 | 59 | ||
82 | .action-button { | 60 | .action-button { |
83 | flex-direction: column; | 61 | margin-top: 10px; |
84 | align-self: center; | 62 | margin-left: auto; |
85 | align-items: center; | ||
86 | margin-left: 0px; | ||
87 | } | ||
88 | |||
89 | my-edit-button { | ||
90 | margin: 15px 0 5px 0; | ||
91 | width: 100%; | ||
92 | text-align: center; | ||
93 | |||
94 | ::ng-deep { | ||
95 | .action-button { | ||
96 | /* same width than a.video-thumbnail */ | ||
97 | width: $video-thumbnail-width; | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | |||
102 | ::ng-deep { | ||
103 | .video-miniature { | ||
104 | align-items: center; | ||
105 | |||
106 | .video-bottom, | ||
107 | .video-bottom .video-miniature-information { | ||
108 | /* same width than a.video-thumbnail */ | ||
109 | max-width: $video-thumbnail-width !important; | ||
110 | } | ||
111 | } | ||
112 | } | 63 | } |
113 | } | 64 | } |
114 | 65 | ||
115 | // Adapt my-video-miniature on small screens with menu | 66 | @include on-mobile-main-col { |
116 | @media screen and (min-width: $small-view) and (max-width: #{breakpoint(lg) + ($not-expanded-horizontal-margins / 3) * 2}) { | ||
117 | :host-context(.main-col:not(.expanded)) { | ||
118 | ::ng-deep { | ||
119 | .video-miniature { | ||
120 | flex-direction: column; | ||
121 | |||
122 | .video-miniature-name { | ||
123 | max-width: $video-thumbnail-width; | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | |||
130 | @media screen and (max-width: $mobile-view) { | ||
131 | .videos-header { | 67 | .videos-header { |
132 | flex-direction: column; | 68 | flex-direction: column; |
133 | 69 | ||
134 | input[type=text] { | 70 | input[type=text] { |
135 | width: 100% !important; | 71 | width: 100%; |
72 | margin-bottom: 12px; | ||
73 | } | ||
74 | .peertube-select-container { | ||
75 | margin-left: 0; | ||
136 | } | 76 | } |
137 | } | 77 | } |
78 | |||
79 | .action-button { | ||
80 | margin-left: 0; | ||
81 | } | ||
138 | } | 82 | } |
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.ts b/client/src/app/+my-library/my-videos/my-videos.component.ts index 6a2a62608..356e158d6 100644 --- a/client/src/app/+my-library/my-videos/my-videos.component.ts +++ b/client/src/app/+my-library/my-videos/my-videos.component.ts | |||
@@ -2,12 +2,12 @@ import { concat, Observable, Subject } from 'rxjs' | |||
2 | import { debounceTime, tap, toArray } from 'rxjs/operators' | 2 | import { debounceTime, tap, toArray } from 'rxjs/operators' |
3 | import { Component, OnInit, ViewChild } from '@angular/core' | 3 | import { Component, OnInit, ViewChild } from '@angular/core' |
4 | import { ActivatedRoute, Router } from '@angular/router' | 4 | import { ActivatedRoute, Router } from '@angular/router' |
5 | import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User, UserService } from '@app/core' | 5 | import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User } from '@app/core' |
6 | import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' | 6 | import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' |
7 | import { immutableAssign } from '@app/helpers' | 7 | import { immutableAssign } from '@app/helpers' |
8 | import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' | 8 | import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' |
9 | import { LiveStreamInformationComponent } from '@app/shared/shared-video-live' | 9 | import { LiveStreamInformationComponent } from '@app/shared/shared-video-live' |
10 | import { MiniatureDisplayOptions, OwnerDisplayType, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature' | 10 | import { MiniatureDisplayOptions, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature' |
11 | import { VideoSortField } from '@shared/models' | 11 | import { VideoSortField } from '@shared/models' |
12 | import { VideoChangeOwnershipComponent } from './modals/video-change-ownership.component' | 12 | import { VideoChangeOwnershipComponent } from './modals/video-change-ownership.component' |
13 | 13 | ||
@@ -36,7 +36,6 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook { | |||
36 | state: true, | 36 | state: true, |
37 | blacklistInfo: true | 37 | blacklistInfo: true |
38 | } | 38 | } |
39 | ownerDisplayType: OwnerDisplayType = 'videoChannel' | ||
40 | 39 | ||
41 | videoActions: DropdownAction<{ video: Video }>[] = [] | 40 | videoActions: DropdownAction<{ video: Video }>[] = [] |
42 | 41 | ||
@@ -44,6 +43,7 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook { | |||
44 | videosSearch: string | 43 | videosSearch: string |
45 | videosSearchChanged = new Subject<string>() | 44 | videosSearchChanged = new Subject<string>() |
46 | getVideosObservableFunction = this.getVideosObservable.bind(this) | 45 | getVideosObservableFunction = this.getVideosObservable.bind(this) |
46 | sort: VideoSortField = '-publishedAt' | ||
47 | 47 | ||
48 | user: User | 48 | user: User |
49 | 49 | ||
@@ -81,6 +81,10 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook { | |||
81 | this.videosSearchChanged.next() | 81 | this.videosSearchChanged.next() |
82 | } | 82 | } |
83 | 83 | ||
84 | onChangeSortColumn () { | ||
85 | this.videosSelection.reloadVideos() | ||
86 | } | ||
87 | |||
84 | disableForReuse () { | 88 | disableForReuse () { |
85 | this.videosSelection.disableForReuse() | 89 | this.videosSelection.disableForReuse() |
86 | } | 90 | } |
@@ -89,10 +93,10 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook { | |||
89 | this.videosSelection.enabledForReuse() | 93 | this.videosSelection.enabledForReuse() |
90 | } | 94 | } |
91 | 95 | ||
92 | getVideosObservable (page: number, sort: VideoSortField) { | 96 | getVideosObservable (page: number) { |
93 | const newPagination = immutableAssign(this.pagination, { currentPage: page }) | 97 | const newPagination = immutableAssign(this.pagination, { currentPage: page }) |
94 | 98 | ||
95 | return this.videoService.getMyVideos(newPagination, sort, this.videosSearch) | 99 | return this.videoService.getMyVideos(newPagination, this.sort, this.videosSearch) |
96 | .pipe( | 100 | .pipe( |
97 | tap(res => this.pagination.totalItems = res.total) | 101 | tap(res => this.pagination.totalItems = res.total) |
98 | ) | 102 | ) |
diff --git a/client/src/app/+search/search.component.html b/client/src/app/+search/search.component.html index 84be4fb14..65d4b6ecd 100644 --- a/client/src/app/+search/search.component.html +++ b/client/src/app/+search/search.component.html | |||
@@ -2,14 +2,12 @@ | |||
2 | <div class="results-header"> | 2 | <div class="results-header"> |
3 | <div class="first-line"> | 3 | <div class="first-line"> |
4 | <div class="results-counter" *ngIf="pagination.totalItems"> | 4 | <div class="results-counter" *ngIf="pagination.totalItems"> |
5 | <span i18n>{{ pagination.totalItems | myNumberFormatter }} {pagination.totalItems, plural, =1 {result} other {results}} </span> | 5 | <span class="mr-1" i18n>{{ pagination.totalItems | myNumberFormatter }} {pagination.totalItems, plural, =1 {result} other {results}}</span> |
6 | 6 | ||
7 | <span i18n *ngIf="advancedSearch.searchTarget === 'local'">on this instance</span> | 7 | <span class="mr-1" i18n *ngIf="advancedSearch.searchTarget === 'local'">on this instance</span> |
8 | <span i18n *ngIf="advancedSearch.searchTarget === 'search-index'">on the vidiverse</span> | 8 | <span class="mr-1" i18n *ngIf="advancedSearch.searchTarget === 'search-index'">on the vidiverse</span> |
9 | 9 | ||
10 | <span *ngIf="currentSearch" i18n> | 10 | <span *ngIf="currentSearch" i18n>for <span class="search-value">{{ currentSearch }}</span></span> |
11 | for <span class="search-value">{{ currentSearch }}</span> | ||
12 | </span> | ||
13 | </div> | 11 | </div> |
14 | 12 | ||
15 | <div | 13 | <div |
@@ -35,11 +33,11 @@ | |||
35 | 33 | ||
36 | <ng-container *ngFor="let result of results"> | 34 | <ng-container *ngFor="let result of results"> |
37 | <div *ngIf="isVideoChannel(result)" class="entry video-channel"> | 35 | <div *ngIf="isVideoChannel(result)" class="entry video-channel"> |
38 | <a *ngIf="!isExternalChannelUrl()" [routerLink]="getChannelUrl(result)"> | 36 | <a class="link-avatar" *ngIf="!isExternalChannelUrl()" [routerLink]="getChannelUrl(result)"> |
39 | <img [src]="result.avatarUrl" alt="Avatar" /> | 37 | <img [src]="result.avatarUrl" alt="Avatar" /> |
40 | </a> | 38 | </a> |
41 | 39 | ||
42 | <a *ngIf="isExternalChannelUrl()" [href]="getChannelUrl(result)" target="_blank"> | 40 | <a class="link-avatar" *ngIf="isExternalChannelUrl()" [href]="getChannelUrl(result)" target="_blank"> |
43 | <img [src]="result.avatarUrl" alt="Avatar" /> | 41 | <img [src]="result.avatarUrl" alt="Avatar" /> |
44 | </a> | 42 | </a> |
45 | 43 | ||
diff --git a/client/src/app/+search/search.component.scss b/client/src/app/+search/search.component.scss index 64927fa4b..91c8272d7 100644 --- a/client/src/app/+search/search.component.scss +++ b/client/src/app/+search/search.component.scss | |||
@@ -1,159 +1,122 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | @mixin build-channel-img-size ($video-img-width) { | ||
5 | $image-size: min(130px, $video-img-width); | ||
6 | $margin-size: ($video-img-width - $image-size) / 2; // So we have the same width than the video miniature | ||
7 | |||
8 | @include channel-avatar($image-size); | ||
9 | |||
10 | margin: 0 $margin-size 0 $margin-size; | ||
11 | } | ||
12 | |||
4 | .search-result { | 13 | .search-result { |
5 | padding: 40px; | 14 | padding: 40px; |
15 | } | ||
6 | 16 | ||
7 | .results-header { | 17 | .results-header { |
8 | font-size: 16px; | 18 | font-size: 16px; |
9 | padding-bottom: 20px; | 19 | padding-bottom: 20px; |
10 | margin-bottom: 30px; | 20 | margin-bottom: 30px; |
11 | border-bottom: 1px solid #DADADA; | 21 | border-bottom: 1px solid #DADADA; |
12 | 22 | ||
13 | .first-line { | 23 | .first-line { |
14 | display: flex; | 24 | display: flex; |
15 | flex-direction: row; | 25 | flex-direction: row; |
16 | 26 | ||
17 | .results-counter { | 27 | .results-counter { |
18 | flex-grow: 1; | 28 | flex-grow: 1; |
19 | 29 | ||
20 | .search-value { | 30 | .search-value { |
21 | font-weight: $font-semibold; | 31 | font-weight: $font-semibold; |
22 | } | ||
23 | } | 32 | } |
33 | } | ||
24 | 34 | ||
25 | .results-filter-button { | 35 | .results-filter-button { |
26 | cursor: pointer; | 36 | cursor: pointer; |
27 | 37 | ||
28 | .icon.icon-filter { | 38 | .icon.icon-filter { |
29 | @include icon(20px); | 39 | @include icon(20px); |
30 | 40 | ||
31 | position: relative; | 41 | position: relative; |
32 | top: -1px; | 42 | top: -1px; |
33 | margin-right: 5px; | 43 | margin-right: 5px; |
34 | background-image: url('../../assets/images/feather/filter.svg'); | 44 | background-image: url('../../assets/images/feather/filter.svg'); |
35 | } | ||
36 | } | 45 | } |
37 | } | 46 | } |
38 | } | 47 | } |
48 | } | ||
39 | 49 | ||
40 | .entry { | 50 | .entry { |
41 | display: flex; | 51 | display: flex; |
42 | min-height: 130px; | 52 | margin-bottom: 40px; |
43 | padding-bottom: 20px; | 53 | max-width: 800px; |
44 | margin-bottom: 20px; | 54 | } |
45 | 55 | ||
46 | &.video-channel { | 56 | .video-channel { |
47 | img { | 57 | img { |
48 | $image-size: 130px; | 58 | @include build-channel-img-size($video-thumbnail-width); |
49 | $margin-size: ($video-thumbnail-width - $image-size) / 2; // So we have the same width than the video miniature | 59 | } |
60 | } | ||
50 | 61 | ||
51 | @include avatar($image-size); | 62 | .video-channel-info { |
63 | flex-grow: 1; | ||
64 | margin: 0 10px; | ||
65 | width: fit-content; | ||
66 | } | ||
52 | 67 | ||
53 | margin: 0 ($margin-size + 10) 0 $margin-size; | 68 | .video-channel-names { |
54 | } | 69 | @include disable-default-a-behaviour; |
55 | 70 | ||
56 | .video-channel-info { | 71 | display: flex; |
57 | flex-grow: 1; | 72 | align-items: baseline; |
58 | width: fit-content; | 73 | color: pvar(--mainForegroundColor); |
59 | 74 | width: fit-content; | |
60 | .video-channel-names { | ||
61 | @include disable-default-a-behaviour; | ||
62 | |||
63 | display: flex; | ||
64 | align-items: baseline; | ||
65 | color: pvar(--mainForegroundColor); | ||
66 | width: fit-content; | ||
67 | |||
68 | .video-channel-display-name { | ||
69 | font-weight: $font-semibold; | ||
70 | font-size: 18px; | ||
71 | } | ||
72 | |||
73 | .video-channel-name { | ||
74 | font-size: 14px; | ||
75 | color: $grey-actor-name; | ||
76 | margin-left: 5px; | ||
77 | } | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | } | 75 | } |
83 | 76 | ||
84 | @media screen and (min-width: $small-view) and (max-width: breakpoint(xl)) { | 77 | .video-channel-display-name { |
85 | .video-channel-info .video-channel-names { | 78 | font-weight: $font-semibold; |
86 | flex-direction: column !important; | 79 | font-size: $video-miniature-row-name-font-size; |
80 | } | ||
87 | 81 | ||
88 | .video-channel-name { | 82 | .video-channel-name { |
89 | @include ellipsis; // Ellipsis and max-width on channel-name to not break screen | 83 | font-size: $video-miniature-row-info-font-size; |
84 | color: pvar(--greyForegroundColor); | ||
85 | margin-left: 5px; | ||
86 | } | ||
90 | 87 | ||
91 | max-width: 250px; | 88 | // Use the same breakpoints than in video-miniature |
92 | margin-left: 0 !important; | 89 | @include on-small-main-col { |
93 | } | 90 | .video-channel { |
94 | } | 91 | display: grid; |
92 | grid-template-columns: auto 1fr; | ||
93 | grid-template-rows: auto auto; | ||
95 | 94 | ||
96 | :host-context(.main-col:not(.expanded)) { | 95 | .link-avatar { |
97 | // Override the min-width: 500px to not break screen | 96 | grid-column: 1; |
98 | ::ng-deep .video-miniature-information { | 97 | grid-row: 1 / -1; |
99 | min-width: 300px !important; | ||
100 | } | 98 | } |
101 | } | ||
102 | } | ||
103 | 99 | ||
104 | @media screen and (min-width: $small-view) and (max-width: breakpoint(lg)) { | 100 | img { |
105 | :host-context(.main-col:not(.expanded)) { | 101 | @include build-channel-img-size($video-thumbnail-medium-width); |
106 | .video-channel-info .video-channel-names { | ||
107 | .video-channel-name { | ||
108 | max-width: 160px; | ||
109 | } | ||
110 | } | 102 | } |
103 | } | ||
111 | 104 | ||
112 | // Override the min-width: 500px to not break screen | 105 | .video-channel-info { |
113 | ::ng-deep .video-miniature-information { | 106 | grid-column: 2; |
114 | min-width: $video-thumbnail-width !important; | 107 | grid-row: 1; |
115 | } | ||
116 | } | 108 | } |
117 | 109 | ||
118 | :host-context(.expanded) { | 110 | my-subscribe-button { |
119 | // Override the min-width: 500px to not break screen | 111 | grid-column: 2; |
120 | ::ng-deep .video-miniature-information { | 112 | grid-row: 2; |
121 | min-width: 300px !important; | 113 | align-self: end; |
122 | } | ||
123 | } | 114 | } |
124 | } | 115 | } |
125 | 116 | ||
126 | @media screen and (max-width: $small-view) { | 117 | @include on-mobile-main-col { |
127 | .search-result { | 118 | .video-channel img { |
128 | .entry.video-channel, | 119 | @include build-channel-img-size($video-thumbnail-small-width); |
129 | .entry.video { | ||
130 | flex-direction: column; | ||
131 | height: auto; | ||
132 | justify-content: center; | ||
133 | align-items: center; | ||
134 | text-align: center; | ||
135 | |||
136 | img { | ||
137 | margin: 0; | ||
138 | } | ||
139 | |||
140 | img { | ||
141 | margin: 0; | ||
142 | } | ||
143 | |||
144 | .video-channel-info .video-channel-names { | ||
145 | align-items: center; | ||
146 | flex-direction: column !important; | ||
147 | |||
148 | .video-channel-name { | ||
149 | margin-left: 0 !important; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | my-subscribe-button { | ||
154 | margin-top: 5px; | ||
155 | } | ||
156 | } | ||
157 | } | 120 | } |
158 | } | 121 | } |
159 | 122 | ||
@@ -164,28 +127,13 @@ | |||
164 | .results-header { | 127 | .results-header { |
165 | font-size: 15px !important; | 128 | font-size: 15px !important; |
166 | } | 129 | } |
130 | } | ||
167 | 131 | ||
168 | .entry { | 132 | .video-channel-display-name { |
169 | &.video { | 133 | font-size: $video-miniature-row-mobile-name-font-size; |
170 | .video-info-name, | 134 | } |
171 | .video-info-account { | 135 | |
172 | margin: auto; | 136 | .video-channel-name { |
173 | } | 137 | font-size: $video-miniature-row-mobile-info-font-size; |
174 | |||
175 | my-video-thumbnail { | ||
176 | margin-right: 0 !important; | ||
177 | |||
178 | ::ng-deep .video-thumbnail { | ||
179 | width: 100%; | ||
180 | height: auto; | ||
181 | |||
182 | img { | ||
183 | width: 100%; | ||
184 | height: auto; | ||
185 | } | ||
186 | } | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | } | 138 | } |
191 | } | 139 | } |
diff --git a/client/src/app/+video-channels/video-channel-about/video-channel-about.component.html b/client/src/app/+video-channels/video-channel-about/video-channel-about.component.html deleted file mode 100644 index 8dff8ba91..000000000 --- a/client/src/app/+video-channels/video-channel-about/video-channel-about.component.html +++ /dev/null | |||
@@ -1,22 +0,0 @@ | |||
1 | <div class="margin-content"> | ||
2 | <div *ngIf="videoChannel" class="row no-gutters"> | ||
3 | <div class="description col-md-6 col-sm-12 pr-2"> | ||
4 | <div class="block"> | ||
5 | <div i18n class="small-title">DESCRIPTION</div> | ||
6 | <div class="content" [innerHtml]="getVideoChannelDescription()"></div> | ||
7 | </div> | ||
8 | |||
9 | <div class="block" *ngIf="supportHTML"> | ||
10 | <div i18n class="small-title">SUPPORT THIS CHANNEL</div> | ||
11 | <div class="content" [innerHtml]="supportHTML"></div> | ||
12 | </div> | ||
13 | </div> | ||
14 | |||
15 | <div class="stats col-md-6 col-sm-12"> | ||
16 | <div class="block"> | ||
17 | <div i18n class="small-title">STATS</div> | ||
18 | <div i18n class="content">Created {{ videoChannel.createdAt | date }}</div> | ||
19 | </div> | ||
20 | </div> | ||
21 | </div> | ||
22 | </div> | ||
diff --git a/client/src/app/+video-channels/video-channel-about/video-channel-about.component.scss b/client/src/app/+video-channels/video-channel-about/video-channel-about.component.scss deleted file mode 100644 index 5bcd4b561..000000000 --- a/client/src/app/+video-channels/video-channel-about/video-channel-about.component.scss +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .block { | ||
5 | margin-bottom: 40px; | ||
6 | |||
7 | .small-title { | ||
8 | @include in-content-small-title; | ||
9 | |||
10 | margin-bottom: 20px; | ||
11 | } | ||
12 | } | ||
diff --git a/client/src/app/+video-channels/video-channel-about/video-channel-about.component.ts b/client/src/app/+video-channels/video-channel-about/video-channel-about.component.ts deleted file mode 100644 index 537c7d08e..000000000 --- a/client/src/app/+video-channels/video-channel-about/video-channel-about.component.ts +++ /dev/null | |||
@@ -1,43 +0,0 @@ | |||
1 | import { Subscription } from 'rxjs' | ||
2 | import { Component, OnDestroy, OnInit } from '@angular/core' | ||
3 | import { MarkdownService } from '@app/core' | ||
4 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' | ||
5 | |||
6 | @Component({ | ||
7 | selector: 'my-video-channel-about', | ||
8 | templateUrl: './video-channel-about.component.html', | ||
9 | styleUrls: [ './video-channel-about.component.scss' ] | ||
10 | }) | ||
11 | export class VideoChannelAboutComponent implements OnInit, OnDestroy { | ||
12 | videoChannel: VideoChannel | ||
13 | descriptionHTML = '' | ||
14 | supportHTML = '' | ||
15 | |||
16 | private videoChannelSub: Subscription | ||
17 | |||
18 | constructor ( | ||
19 | private videoChannelService: VideoChannelService, | ||
20 | private markdownService: MarkdownService | ||
21 | ) { } | ||
22 | |||
23 | ngOnInit () { | ||
24 | // Parent get the video channel for us | ||
25 | this.videoChannelSub = this.videoChannelService.videoChannelLoaded | ||
26 | .subscribe(async videoChannel => { | ||
27 | this.videoChannel = videoChannel | ||
28 | |||
29 | this.descriptionHTML = await this.markdownService.textMarkdownToHTML(this.videoChannel.description) | ||
30 | this.supportHTML = await this.markdownService.enhancedMarkdownToHTML(this.videoChannel.support) | ||
31 | }) | ||
32 | } | ||
33 | |||
34 | ngOnDestroy () { | ||
35 | if (this.videoChannelSub) this.videoChannelSub.unsubscribe() | ||
36 | } | ||
37 | |||
38 | getVideoChannelDescription () { | ||
39 | if (this.descriptionHTML) return this.descriptionHTML | ||
40 | |||
41 | return $localize`No description` | ||
42 | } | ||
43 | } | ||
diff --git a/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.html b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.html index 03770ceec..b69d1682a 100644 --- a/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.html +++ b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.html | |||
@@ -1,13 +1,13 @@ | |||
1 | <div class="margin-content"> | 1 | <div class="margin-content"> |
2 | <div i18n class="title-page title-page-single"> | 2 | <div i18n class="title-page title-page-single" *ngIf="pagination.totalItems"> |
3 | Created {{ pagination.totalItems }} playlists | 3 | Created {pagination.totalItems, plural, =1 {1 playlist} other {{{ pagination.totalItems }} playlists}} |
4 | </div> | 4 | </div> |
5 | 5 | ||
6 | <div i18n class="no-results" *ngIf="pagination.totalItems === 0">This channel does not have playlists.</div> | 6 | <div i18n class="no-results" *ngIf="pagination.totalItems === 0">This channel does not have playlists.</div> |
7 | 7 | ||
8 | <div class="video-playlist" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()"> | 8 | <div class="playlists" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()"> |
9 | <div *ngFor="let playlist of videoPlaylists" class="playlist-miniature-container"> | 9 | <div *ngFor="let playlist of videoPlaylists" class="playlist-wrapper"> |
10 | <my-video-playlist-miniature [playlist]="playlist" [toManage]="false"></my-video-playlist-miniature> | 10 | <my-video-playlist-miniature [playlist]="playlist" [toManage]="false" [displayAsRow]="displayAsRow()"></my-video-playlist-miniature> |
11 | </div> | 11 | </div> |
12 | </div> | 12 | </div> |
13 | </div> | 13 | </div> |
diff --git a/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.scss b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.scss index cb2931858..acd2e409e 100644 --- a/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.scss +++ b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.scss | |||
@@ -1,14 +1,32 @@ | |||
1 | .title-page { | 1 | @import '_variables'; |
2 | margin-top: 0; | 2 | @import '_mixins'; |
3 | } | 3 | @import '_miniature'; |
4 | 4 | ||
5 | .video-playlist { | 5 | .playlists { |
6 | display: flex; | 6 | display: flex; |
7 | flex-wrap: wrap; | 7 | flex-wrap: wrap; |
8 | justify-content: center; | 8 | justify-content: center; |
9 | 9 | ||
10 | .playlist-miniature-container { | 10 | .playlist-wrapper { |
11 | margin-right: 15px; | 11 | margin-right: 15px; |
12 | margin-bottom: 30px; | 12 | margin-bottom: 30px; |
13 | } | 13 | } |
14 | } | 14 | } |
15 | |||
16 | .margin-content { | ||
17 | @include grid-videos-miniature-layout; | ||
18 | } | ||
19 | |||
20 | @media screen and (max-width: $mobile-view) { | ||
21 | .title-page { | ||
22 | display: block; | ||
23 | text-align: center; | ||
24 | } | ||
25 | |||
26 | .playlists { | ||
27 | justify-content: left; | ||
28 | |||
29 | margin-left: pvar(--horizontalMarginContent) !important; | ||
30 | margin-right: pvar(--horizontalMarginContent) !important; | ||
31 | } | ||
32 | } | ||
diff --git a/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts index 8b507c626..14465bb8d 100644 --- a/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts +++ b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { Subject, Subscription } from 'rxjs' | 1 | import { Subject, Subscription } from 'rxjs' |
2 | import { Component, OnDestroy, OnInit } from '@angular/core' | 2 | import { Component, OnDestroy, OnInit } from '@angular/core' |
3 | import { ComponentPagination, hasMoreItems } from '@app/core' | 3 | import { ComponentPagination, hasMoreItems, ScreenService } from '@app/core' |
4 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' | 4 | import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' |
5 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' | 5 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' |
6 | 6 | ||
@@ -25,7 +25,8 @@ export class VideoChannelPlaylistsComponent implements OnInit, OnDestroy { | |||
25 | 25 | ||
26 | constructor ( | 26 | constructor ( |
27 | private videoPlaylistService: VideoPlaylistService, | 27 | private videoPlaylistService: VideoPlaylistService, |
28 | private videoChannelService: VideoChannelService | 28 | private videoChannelService: VideoChannelService, |
29 | private screenService: ScreenService | ||
29 | ) {} | 30 | ) {} |
30 | 31 | ||
31 | ngOnInit () { | 32 | ngOnInit () { |
@@ -48,6 +49,10 @@ export class VideoChannelPlaylistsComponent implements OnInit, OnDestroy { | |||
48 | this.loadVideoPlaylists() | 49 | this.loadVideoPlaylists() |
49 | } | 50 | } |
50 | 51 | ||
52 | displayAsRow () { | ||
53 | return this.screenService.isInMobileView() | ||
54 | } | ||
55 | |||
51 | private loadVideoPlaylists () { | 56 | private loadVideoPlaylists () { |
52 | this.videoPlaylistService.listChannelPlaylists(this.videoChannel, this.pagination) | 57 | this.videoPlaylistService.listChannelPlaylists(this.videoChannel, this.pagination) |
53 | .subscribe(res => { | 58 | .subscribe(res => { |
diff --git a/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts b/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts index 803651505..d83fc1324 100644 --- a/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts +++ b/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts | |||
@@ -5,7 +5,7 @@ import { ActivatedRoute, Router } from '@angular/router' | |||
5 | import { AuthService, ConfirmService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core' | 5 | import { AuthService, ConfirmService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core' |
6 | import { immutableAssign } from '@app/helpers' | 6 | import { immutableAssign } from '@app/helpers' |
7 | import { VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' | 7 | import { VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' |
8 | import { AbstractVideoList } from '@app/shared/shared-video-miniature' | 8 | import { AbstractVideoList, MiniatureDisplayOptions } from '@app/shared/shared-video-miniature' |
9 | import { VideoFilter } from '@shared/models' | 9 | import { VideoFilter } from '@shared/models' |
10 | 10 | ||
11 | @Component({ | 11 | @Component({ |
@@ -16,12 +16,24 @@ import { VideoFilter } from '@shared/models' | |||
16 | ] | 16 | ] |
17 | }) | 17 | }) |
18 | export class VideoChannelVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { | 18 | export class VideoChannelVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { |
19 | // No value because we don't want a page title | ||
19 | titlePage: string | 20 | titlePage: string |
20 | loadOnInit = false | 21 | loadOnInit = false |
21 | loadUserVideoPreferences = true | 22 | loadUserVideoPreferences = true |
22 | 23 | ||
23 | filter: VideoFilter = null | 24 | filter: VideoFilter = null |
24 | 25 | ||
26 | displayOptions: MiniatureDisplayOptions = { | ||
27 | date: true, | ||
28 | views: true, | ||
29 | by: false, | ||
30 | avatar: false, | ||
31 | privacyLabel: true, | ||
32 | privacyText: false, | ||
33 | state: false, | ||
34 | blacklistInfo: false | ||
35 | } | ||
36 | |||
25 | private videoChannel: VideoChannel | 37 | private videoChannel: VideoChannel |
26 | private videoChannelSub: Subscription | 38 | private videoChannelSub: Subscription |
27 | 39 | ||
@@ -83,13 +95,6 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On | |||
83 | 95 | ||
84 | return this.videoService | 96 | return this.videoService |
85 | .getVideoChannelVideos(options) | 97 | .getVideoChannelVideos(options) |
86 | .pipe( | ||
87 | tap(({ total }) => { | ||
88 | this.titlePage = total === 1 | ||
89 | ? $localize`Published 1 video` | ||
90 | : $localize`Published ${total} videos` | ||
91 | }) | ||
92 | ) | ||
93 | } | 98 | } |
94 | 99 | ||
95 | generateSyndicationList () { | 100 | generateSyndicationList () { |
@@ -101,4 +106,8 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On | |||
101 | 106 | ||
102 | this.reloadVideos() | 107 | this.reloadVideos() |
103 | } | 108 | } |
109 | |||
110 | displayAsRow () { | ||
111 | return this.screenService.isInMobileView() | ||
112 | } | ||
104 | } | 113 | } |
diff --git a/client/src/app/+video-channels/video-channels-routing.module.ts b/client/src/app/+video-channels/video-channels-routing.module.ts index f8c32f14e..fcaad8934 100644 --- a/client/src/app/+video-channels/video-channels-routing.module.ts +++ b/client/src/app/+video-channels/video-channels-routing.module.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import { NgModule } from '@angular/core' | 1 | import { NgModule } from '@angular/core' |
2 | import { RouterModule, Routes } from '@angular/router' | 2 | import { RouterModule, Routes } from '@angular/router' |
3 | import { MetaGuard } from '@ngx-meta/core' | 3 | import { MetaGuard } from '@ngx-meta/core' |
4 | import { VideoChannelAboutComponent } from './video-channel-about/video-channel-about.component' | ||
5 | import { VideoChannelPlaylistsComponent } from './video-channel-playlists/video-channel-playlists.component' | 4 | import { VideoChannelPlaylistsComponent } from './video-channel-playlists/video-channel-playlists.component' |
6 | import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component' | 5 | import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component' |
7 | import { VideoChannelsComponent } from './video-channels.component' | 6 | import { VideoChannelsComponent } from './video-channels.component' |
@@ -38,15 +37,6 @@ const videoChannelsRoutes: Routes = [ | |||
38 | title: $localize`Video channel playlists` | 37 | title: $localize`Video channel playlists` |
39 | } | 38 | } |
40 | } | 39 | } |
41 | }, | ||
42 | { | ||
43 | path: 'about', | ||
44 | component: VideoChannelAboutComponent, | ||
45 | data: { | ||
46 | meta: { | ||
47 | title: $localize`About video channel` | ||
48 | } | ||
49 | } | ||
50 | } | 40 | } |
51 | ] | 41 | ] |
52 | } | 42 | } |
diff --git a/client/src/app/+video-channels/video-channels.component.html b/client/src/app/+video-channels/video-channels.component.html index 4b0d12b6e..1312a1b3c 100644 --- a/client/src/app/+video-channels/video-channels.component.html +++ b/client/src/app/+video-channels/video-channels.component.html | |||
@@ -1,50 +1,129 @@ | |||
1 | <div *ngIf="videoChannel" class="row"> | 1 | <div class="root" *ngIf="videoChannel"> |
2 | <div class="sub-menu"> | 2 | <div class="banner" *ngIf="videoChannel.bannerUrl"> |
3 | 3 | <img [src]="videoChannel.bannerUrl" alt="Channel banner"> | |
4 | <div class="actor"> | 4 | </div> |
5 | <img [src]="videoChannel.avatarUrl" alt="Avatar" /> | 5 | |
6 | 6 | <div class="channel-info"> | |
7 | <div class="actor-info"> | 7 | |
8 | <div class="actor-names"> | 8 | <ng-template #buttonsTemplate> |
9 | <div class="actor-display-name">{{ videoChannel.displayName }}</div> | 9 | <a *ngIf="isManageable()" [routerLink]="[ '/my-library/video-channels/update', videoChannel.nameWithHost ]" class="peertube-button-link orange-button" i18n> |
10 | <div class="actor-name"> | 10 | Manage channel |
11 | <span>{{ videoChannel.nameWithHost }}</span> | 11 | </a> |
12 | <button [cdkCopyToClipboard]="videoChannel.nameWithHostForced" (click)="activateCopiedMessage()" | 12 | |
13 | class="btn btn-outline-secondary btn-sm copy-button" | 13 | <my-subscribe-button *ngIf="!isManageable()" #subscribeButton [videoChannels]="[videoChannel]"></my-subscribe-button> |
14 | > | 14 | |
15 | <span class="glyphicon glyphicon-copy"></span> | 15 | <button *ngIf="videoChannel.support" (click)="showSupportModal()" class="support-button peertube-button orange-button-inverted"> |
16 | </button> | 16 | <my-global-icon iconName="support" aria-hidden="true"></my-global-icon> |
17 | <span class="icon-text" i18n>Support</span> | ||
18 | </button> | ||
19 | </ng-template> | ||
20 | |||
21 | <ng-template #ownerTemplate> | ||
22 | <div class="owner-block"> | ||
23 | <div class="avatar-row"> | ||
24 | <a [routerLink]="getAccountUrl()" title="View account" i18n-title> | ||
25 | <img class="account-avatar" [src]="videoChannel.ownerAvatarUrl" alt="Owner account avatar" /> | ||
26 | </a> | ||
27 | |||
28 | <div class="actor-info"> | ||
29 | <h4> | ||
30 | <a [routerLink]="getAccountUrl()" title="View account" i18n-title>{{ videoChannel.ownerAccount.displayName }}</a> | ||
31 | </h4> | ||
32 | |||
33 | <div class="actor-handle">@{{ videoChannel.ownerBy }}</div> | ||
17 | </div> | 34 | </div> |
18 | </div> | 35 | </div> |
19 | 36 | ||
20 | <div class="right-buttons"> | 37 | <div class="owner-description"> |
21 | <a *ngIf="isChannelManageable && !isInSmallView" [routerLink]="[ '/my-library/video-channels/update', videoChannel.nameWithHost ]" class="btn btn-outline-tertiary mr-2" i18n> | 38 | <div class="description-html" [innerHTML]="ownerDescriptionHTML"></div> |
22 | Manage channel | ||
23 | </a> | ||
24 | <my-subscribe-button #subscribeButton [videoChannels]="[videoChannel]"></my-subscribe-button> | ||
25 | </div> | 39 | </div> |
26 | 40 | ||
27 | <div class="actor-lower"> | 41 | <a class="view-account short" [routerLink]="getAccountUrl()" i18n> |
28 | <div class="actor-followers" i18n>{videoChannel.followersCount, plural, =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</div> | 42 | View account |
43 | </a> | ||
29 | 44 | ||
30 | <a [routerLink]="[ '/accounts', videoChannel.ownerBy ]" i18n-title title="Go the owner account page" class="actor-owner"> | 45 | <a class="view-account complete" [routerLink]="getAccountUrl()" i18n> |
31 | <span class="d-inline-flex"><span i18n class="d-none d-sm-block mr-1">Created by</span>{{ videoChannel.ownerBy }}</span> | 46 | View owner account |
32 | <img [src]="videoChannel.ownerAvatarUrl" alt="Owner account avatar" /> | 47 | </a> |
33 | </a> | 48 | </div> |
49 | </ng-template> | ||
50 | |||
51 | <div class="channel-avatar-row"> | ||
52 | <img class="channel-avatar" [src]="videoChannel.avatarUrl" alt="Avatar" /> | ||
53 | |||
54 | <div> | ||
55 | <div class="section-label" i18n>VIDEO CHANNEL</div> | ||
56 | |||
57 | <div class="actor-info"> | ||
58 | <div> | ||
59 | <div class="actor-display-name"> | ||
60 | <h1>{{ videoChannel.displayName }}</h1> | ||
61 | </div> | ||
62 | |||
63 | <div class="actor-handle"> | ||
64 | <span>@{{ videoChannel.nameWithHost }}</span> | ||
65 | <button [cdkCopyToClipboard]="videoChannel.nameWithHostForced" (click)="activateCopiedMessage()" | ||
66 | class="btn btn-outline-secondary btn-sm copy-button" title="Copy channel handle" i18n-title | ||
67 | > | ||
68 | <span class="glyphicon glyphicon-duplicate"></span> | ||
69 | </button> | ||
70 | </div> | ||
71 | |||
72 | <div class="actor-counters"> | ||
73 | <span i18n>{videoChannel.followersCount, plural, =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</span> | ||
74 | |||
75 | <span class="videos-count" *ngIf="channelVideosCount !== undefined" i18n> | ||
76 | {channelVideosCount, plural, =1 {1 videos} other {{{ channelVideosCount }} videos}} | ||
77 | </span> | ||
78 | </div> | ||
79 | </div> | ||
80 | |||
81 | <div class="channel-buttons right"> | ||
82 | <ng-template *ngTemplateOutlet="buttonsTemplate"></ng-template> | ||
83 | </div> | ||
34 | </div> | 84 | </div> |
35 | </div> | 85 | </div> |
36 | </div> | 86 | </div> |
37 | 87 | ||
38 | <div class="links w-100"> | 88 | <div class="channel-description" [ngClass]="{ expanded: channelDescriptionExpanded }"> |
39 | <ng-template #linkTemplate let-item="item"> | 89 | <div class="description-html" [innerHTML]="channelDescriptionHTML"></div> |
40 | <a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a> | ||
41 | </ng-template> | ||
42 | 90 | ||
43 | <list-overflow [items]="links" [itemTemplate]="linkTemplate"></list-overflow> | 91 | <div class="created-at" i18n>Channel created on {{ videoChannel.createdAt | date }}</div> |
44 | </div> | 92 | </div> |
93 | |||
94 | <div *ngIf="hasShowMoreDescription()" class="show-more" role="button" | ||
95 | (click)="channelDescriptionExpanded = !channelDescriptionExpanded" | ||
96 | title="Show the complete description" i18n-title i18n | ||
97 | > | ||
98 | Show more... | ||
99 | </div> | ||
100 | |||
101 | <div class="channel-buttons bottom"> | ||
102 | <ng-template *ngTemplateOutlet="buttonsTemplate"></ng-template> | ||
103 | </div> | ||
104 | |||
105 | <div class="owner-card"> | ||
106 | <div class="section-label" i18n>OWNER ACCOUNT</div> | ||
107 | |||
108 | <ng-template *ngTemplateOutlet="ownerTemplate"></ng-template> | ||
109 | </div> | ||
110 | </div> | ||
111 | |||
112 | <div class="bottom-owner"> | ||
113 | <div class="section-label" i18n>OWNER ACCOUNT</div> | ||
114 | |||
115 | <ng-template *ngTemplateOutlet="ownerTemplate"></ng-template> | ||
45 | </div> | 116 | </div> |
46 | 117 | ||
47 | <div class="margin-content"> | 118 | <div class="links"> |
48 | <router-outlet></router-outlet> | 119 | <ng-template #linkTemplate let-item="item"> |
120 | <a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a> | ||
121 | </ng-template> | ||
122 | |||
123 | <list-overflow [items]="links" [itemTemplate]="linkTemplate"></list-overflow> | ||
49 | </div> | 124 | </div> |
125 | |||
126 | <router-outlet></router-outlet> | ||
50 | </div> | 127 | </div> |
128 | |||
129 | <my-support-modal #supportModal [videoChannel]="videoChannel"></my-support-modal> | ||
diff --git a/client/src/app/+video-channels/video-channels.component.scss b/client/src/app/+video-channels/video-channels.component.scss index 22f21dcc6..b19b4c81b 100644 --- a/client/src/app/+video-channels/video-channels.component.scss +++ b/client/src/app/+video-channels/video-channels.component.scss | |||
@@ -1,89 +1,308 @@ | |||
1 | // Bootstrap grid utilities require functions, variables and mixins | ||
2 | @import 'node_modules/bootstrap/scss/functions'; | ||
3 | @import 'node_modules/bootstrap/scss/variables'; | ||
4 | @import 'node_modules/bootstrap/scss/mixins'; | ||
5 | @import 'node_modules/bootstrap/scss/grid'; | ||
6 | |||
7 | @import '_variables'; | 1 | @import '_variables'; |
8 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | @import '_actor'; | ||
4 | @import '_miniature'; | ||
9 | 5 | ||
10 | .sub-menu { | 6 | .root { |
11 | @include sub-menu-with-actor; | 7 | --myGlobalTopPadding: 60px; |
8 | --myChannelImgMargin: 30px; | ||
9 | --myFontSize: 16px; | ||
10 | --myGreyChannelFontSize: 16px; | ||
11 | --myGreyOwnerFontSize: 14px; | ||
12 | } | ||
12 | 13 | ||
13 | .actor, .actor-info { | 14 | .banner { |
14 | width: 100%; | 15 | @include block-ratio('img', $banner-inverted-ratio); |
15 | } | 16 | } |
16 | 17 | ||
17 | .actor-info { | 18 | .section-label { |
18 | display: grid !important; | 19 | @include section-label-responsive; |
19 | grid-template-columns: 1fr auto; | 20 | } |
20 | grid-template-rows: 1fr auto / 1fr auto; | ||
21 | grid-template-areas: "name buttons" "lower buttons"; | ||
22 | 21 | ||
23 | @include media-breakpoint-down(lg) { | 22 | .links { |
24 | grid-template-areas: "name name" "lower buttons"; | 23 | @include grid-videos-miniature-margins; |
25 | } | 24 | } |
25 | |||
26 | .actor-info { | ||
27 | min-width: 1px; | ||
28 | width: 100%; | ||
29 | |||
30 | > h4, | ||
31 | > .actor-handle { | ||
32 | @include ellipsis; | ||
26 | } | 33 | } |
34 | } | ||
35 | |||
36 | .channel-info { | ||
37 | @include grid-videos-miniature-margins(false, 15px); | ||
38 | |||
39 | display: grid; | ||
40 | grid-template-columns: 1fr auto; | ||
41 | grid-template-rows: auto auto; | ||
42 | |||
43 | background-color: pvar(--channelBackgroundColor); | ||
44 | margin-bottom: 45px; | ||
45 | padding-top: var(--myGlobalTopPadding); | ||
46 | font-size: var(--myFontSize); | ||
47 | } | ||
48 | |||
49 | .channel-avatar-row { | ||
50 | @include avatar-row-responsive(var(--myChannelImgMargin), var(--myGreyChannelFontSize)); | ||
51 | } | ||
52 | |||
53 | .support-button { | ||
54 | @include button-with-icon(21px, 0, -1px); | ||
55 | } | ||
56 | |||
57 | .channel-description { | ||
58 | grid-column: 1; | ||
59 | word-break: break-word; | ||
60 | } | ||
61 | |||
62 | .show-more { | ||
63 | @include show-more-description; | ||
64 | |||
65 | display: none; | ||
66 | } | ||
67 | |||
68 | .channel-buttons { | ||
69 | display: flex; | ||
70 | flex-wrap: wrap; | ||
27 | 71 | ||
28 | .actor-names { | 72 | > *:not(:last-child) { |
29 | grid-area: name; | 73 | margin-right: 15px; |
30 | } | 74 | } |
75 | } | ||
76 | |||
77 | .channel-buttons.right { | ||
78 | margin-left: 45px; | ||
79 | } | ||
80 | |||
81 | // Only used by mobile | ||
82 | .channel-buttons.bottom { | ||
83 | display: none; | ||
84 | } | ||
85 | |||
86 | .created-at { | ||
87 | margin-top: 15px; | ||
88 | color: pvar(--greyForegroundColor); | ||
89 | padding-bottom: 60px; | ||
90 | } | ||
91 | |||
92 | .owner-card { | ||
93 | margin-left: 105px; | ||
94 | grid-column: 2; | ||
95 | // Takes all the column | ||
96 | grid-row: 1 / 3; | ||
97 | place-self: end; | ||
98 | } | ||
99 | |||
100 | // Only used on mobile | ||
101 | .bottom-owner { | ||
102 | display: none; | ||
103 | } | ||
104 | |||
105 | .owner-block { | ||
106 | background-color: pvar(--mainBackgroundColor); | ||
107 | padding: 30px; | ||
108 | width: 300px; | ||
109 | font-size: var(--myFontSize); | ||
31 | 110 | ||
32 | .actor-name { | 111 | .avatar-row { |
33 | flex-grow: 1; | 112 | display: flex; |
113 | margin-bottom: 15px; | ||
34 | 114 | ||
35 | .copy-button { | 115 | img { |
36 | border: none; | 116 | @include avatar(48px); |
37 | padding: 5px; | ||
38 | margin-top: -2px; | ||
39 | } | 117 | } |
118 | |||
119 | .actor-info { | ||
120 | margin-left: 15px; | ||
121 | } | ||
122 | |||
123 | h4 { | ||
124 | font-size: 18px; | ||
125 | margin: 0; | ||
126 | |||
127 | a { | ||
128 | color: pvar(--mainForegroundColor); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | .actor-handle { | ||
133 | font-size: var(--myGreyOwnerFontSize); | ||
134 | color: pvar(--greyForegroundColor); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | .owner-description { | ||
139 | max-height: 140px; | ||
140 | word-break: break-word; | ||
141 | |||
142 | @include fade-text(120px, pvar(--mainBackgroundColor)); | ||
40 | } | 143 | } |
41 | } | 144 | } |
42 | 145 | ||
43 | .margin-content { | 146 | .view-account.short { |
44 | // margin-content is required, but child views have their own margins | 147 | @include peertube-button-link; |
45 | // that match views outside the scope of accounts, so we only align | 148 | @include orange-button-inverted; |
46 | // them with the margins of .sub-menu when required. | 149 | |
47 | margin: 0; | 150 | margin-top: 30px; |
151 | } | ||
152 | |||
153 | .view-account.complete { | ||
154 | display: none; | ||
48 | } | 155 | } |
49 | 156 | ||
50 | .right-buttons { | 157 | .copy-button { |
51 | display: flex; | 158 | border: none; |
52 | height: max-content; | 159 | } |
53 | margin-left: auto; | 160 | |
54 | margin-top: 10px; | 161 | @media screen and (max-width: 1400px) { |
162 | // Takes all the row width | ||
163 | .channel-avatar-row { | ||
164 | grid-column: 1 / 3; | ||
165 | } | ||
166 | |||
167 | .owner-card { | ||
168 | grid-row: 2; | ||
169 | margin-left: 60px; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | @media screen and (max-width: 1100px) { | ||
174 | .root { | ||
175 | --myGlobalTopPadding: 45px; | ||
176 | --myChannelImgMargin: 15px; | ||
177 | } | ||
178 | |||
179 | .channel-info { | ||
180 | display: flex; | ||
181 | flex-direction: column; | ||
182 | margin-bottom: 0; | ||
183 | } | ||
184 | |||
185 | .channel-description:not(.expanded) { | ||
186 | max-height: 70px; | ||
187 | |||
188 | @include fade-text(30px, pvar(--channelBackgroundColor)); | ||
189 | } | ||
190 | |||
191 | .show-more { | ||
192 | display: inline-block; | ||
193 | } | ||
194 | |||
195 | .channel-buttons.bottom { | ||
196 | display: flex; | ||
197 | justify-content: center; | ||
198 | margin-bottom: 30px; | ||
199 | } | ||
200 | |||
201 | .channel-buttons.right { | ||
202 | display: none; | ||
203 | } | ||
204 | |||
205 | .owner-card { | ||
206 | display: none; | ||
207 | } | ||
208 | |||
209 | .bottom-owner { | ||
210 | display: block; | ||
211 | width: 100%; | ||
212 | border-bottom: 2px solid $separator-border-color; | ||
213 | padding: var(--myGlobalTopPadding) 45px; | ||
214 | margin-bottom: 60px; | ||
215 | } | ||
55 | 216 | ||
56 | grid-row: buttons-start / span buttons-end; | 217 | .owner-block { |
57 | grid-column: buttons-start; | 218 | display: grid; |
219 | width: 100%; | ||
220 | padding: 0; | ||
58 | 221 | ||
59 | @include media-breakpoint-down(lg) { | 222 | .avatar-row { |
60 | flex-flow: column-reverse; | 223 | grid-column: 1; |
224 | margin-right: 30px; | ||
225 | } | ||
226 | |||
227 | .owner-description { | ||
228 | grid-column: 2; | ||
229 | max-height: 70px; | ||
230 | |||
231 | @include fade-text(30px, pvar(--mainBackgroundColor)); | ||
232 | } | ||
61 | 233 | ||
62 | a { | 234 | .view-account { |
63 | margin-top: 0.25rem; | 235 | grid-column: 2; |
64 | margin-right: 0 !important; | ||
65 | } | 236 | } |
66 | } | 237 | } |
67 | 238 | ||
68 | a { | 239 | .view-account.complete { |
69 | @include peertube-button-outline; | 240 | display: block; |
70 | line-height: 1.8; | 241 | text-align: right; |
242 | margin-top: 10px; | ||
243 | color: pvar(--mainColor); | ||
71 | } | 244 | } |
72 | 245 | ||
73 | my-subscribe-button { | 246 | .view-account.short { |
74 | height: min-content; | 247 | display: none; |
75 | } | 248 | } |
76 | } | 249 | } |
77 | 250 | ||
78 | @media screen and (max-width: $mobile-view) { | 251 | @media screen and (max-width: $mobile-view) { |
79 | .sub-menu { | 252 | .root { |
80 | .actor { | 253 | --myGlobalTopPadding: 15px; |
81 | flex-direction: column; | 254 | --myFontSize: 14px; |
255 | --myGreyChannelFontSize: 13px; | ||
256 | --myGreyOwnerFontSize: 13px; | ||
257 | } | ||
258 | |||
259 | .links { | ||
260 | margin: auto !important; | ||
261 | width: min-content; | ||
262 | } | ||
263 | |||
264 | .show-more { | ||
265 | margin-bottom: 30px; | ||
266 | } | ||
267 | |||
268 | .bottom-owner { | ||
269 | padding: 15px; | ||
270 | margin-bottom: 30px; | ||
82 | 271 | ||
83 | .actor-info .actor-names { | 272 | .section-label { |
273 | display: none; | ||
274 | } | ||
275 | } | ||
276 | |||
277 | .owner-block { | ||
278 | display: block; | ||
279 | |||
280 | .avatar-row { | ||
281 | display: flex; | ||
282 | flex-direction: row-reverse; | ||
283 | margin: 0; | ||
284 | |||
285 | h4 { | ||
286 | font-size: 16px; | ||
287 | } | ||
288 | |||
289 | .actor-info { | ||
290 | display: flex; | ||
84 | flex-direction: column; | 291 | flex-direction: column; |
85 | align-items: normal; | 292 | align-items: flex-end; |
293 | justify-content: flex-end; | ||
294 | margin-top: -5px; | ||
295 | } | ||
296 | |||
297 | img { | ||
298 | @include channel-avatar(64px); | ||
299 | |||
300 | margin: -30px 0 0 15px; | ||
86 | } | 301 | } |
87 | } | 302 | } |
303 | |||
304 | .owner-description { | ||
305 | display: none; | ||
306 | } | ||
88 | } | 307 | } |
89 | } | 308 | } |
diff --git a/client/src/app/+video-channels/video-channels.component.ts b/client/src/app/+video-channels/video-channels.component.ts index bb601e227..41fdb5e79 100644 --- a/client/src/app/+video-channels/video-channels.component.ts +++ b/client/src/app/+video-channels/video-channels.component.ts | |||
@@ -3,8 +3,9 @@ import { Subscription } from 'rxjs' | |||
3 | import { catchError, distinctUntilChanged, map, switchMap } from 'rxjs/operators' | 3 | import { catchError, distinctUntilChanged, map, switchMap } from 'rxjs/operators' |
4 | import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core' | 4 | import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core' |
5 | import { ActivatedRoute } from '@angular/router' | 5 | import { ActivatedRoute } from '@angular/router' |
6 | import { AuthService, Notifier, RestExtractor, ScreenService } from '@app/core' | 6 | import { AuthService, MarkdownService, Notifier, RestExtractor, ScreenService } from '@app/core' |
7 | import { ListOverflowItem, VideoChannel, VideoChannelService } from '@app/shared/shared-main' | 7 | import { ListOverflowItem, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' |
8 | import { SupportModalComponent } from '@app/shared/shared-support-modal' | ||
8 | import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' | 9 | import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' |
9 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | 10 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' |
10 | 11 | ||
@@ -14,12 +15,18 @@ import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | |||
14 | }) | 15 | }) |
15 | export class VideoChannelsComponent implements OnInit, OnDestroy { | 16 | export class VideoChannelsComponent implements OnInit, OnDestroy { |
16 | @ViewChild('subscribeButton') subscribeButton: SubscribeButtonComponent | 17 | @ViewChild('subscribeButton') subscribeButton: SubscribeButtonComponent |
18 | @ViewChild('supportModal') supportModal: SupportModalComponent | ||
17 | 19 | ||
18 | videoChannel: VideoChannel | 20 | videoChannel: VideoChannel |
19 | hotkeys: Hotkey[] | 21 | hotkeys: Hotkey[] |
20 | links: ListOverflowItem[] = [] | 22 | links: ListOverflowItem[] = [] |
21 | isChannelManageable = false | 23 | isChannelManageable = false |
22 | 24 | ||
25 | channelVideosCount: number | ||
26 | ownerDescriptionHTML = '' | ||
27 | channelDescriptionHTML = '' | ||
28 | channelDescriptionExpanded = false | ||
29 | |||
23 | private routeSub: Subscription | 30 | private routeSub: Subscription |
24 | 31 | ||
25 | constructor ( | 32 | constructor ( |
@@ -27,9 +34,11 @@ export class VideoChannelsComponent implements OnInit, OnDestroy { | |||
27 | private notifier: Notifier, | 34 | private notifier: Notifier, |
28 | private authService: AuthService, | 35 | private authService: AuthService, |
29 | private videoChannelService: VideoChannelService, | 36 | private videoChannelService: VideoChannelService, |
37 | private videoService: VideoService, | ||
30 | private restExtractor: RestExtractor, | 38 | private restExtractor: RestExtractor, |
31 | private hotkeysService: HotkeysService, | 39 | private hotkeysService: HotkeysService, |
32 | private screenService: ScreenService | 40 | private screenService: ScreenService, |
41 | private markdown: MarkdownService | ||
33 | ) { } | 42 | ) { } |
34 | 43 | ||
35 | ngOnInit () { | 44 | ngOnInit () { |
@@ -43,16 +52,14 @@ export class VideoChannelsComponent implements OnInit, OnDestroy { | |||
43 | HttpStatusCode.NOT_FOUND_404 | 52 | HttpStatusCode.NOT_FOUND_404 |
44 | ])) | 53 | ])) |
45 | ) | 54 | ) |
46 | .subscribe(videoChannel => { | 55 | .subscribe(async videoChannel => { |
56 | this.channelDescriptionHTML = await this.markdown.textMarkdownToHTML(videoChannel.description) | ||
57 | this.ownerDescriptionHTML = await this.markdown.textMarkdownToHTML(videoChannel.ownerAccount.description) | ||
58 | |||
59 | // After the markdown renderer to avoid layout changes | ||
47 | this.videoChannel = videoChannel | 60 | this.videoChannel = videoChannel |
48 | 61 | ||
49 | if (this.authService.isLoggedIn()) { | 62 | this.loadChannelVideosCount() |
50 | this.authService.userInformationLoaded | ||
51 | .subscribe(() => { | ||
52 | const channelUserId = this.videoChannel.ownerAccount.userId | ||
53 | this.isChannelManageable = channelUserId && channelUserId === this.authService.getUser().id | ||
54 | }) | ||
55 | } | ||
56 | }) | 63 | }) |
57 | 64 | ||
58 | this.hotkeys = [ | 65 | this.hotkeys = [ |
@@ -67,8 +74,7 @@ export class VideoChannelsComponent implements OnInit, OnDestroy { | |||
67 | 74 | ||
68 | this.links = [ | 75 | this.links = [ |
69 | { label: $localize`VIDEOS`, routerLink: 'videos' }, | 76 | { label: $localize`VIDEOS`, routerLink: 'videos' }, |
70 | { label: $localize`VIDEO PLAYLISTS`, routerLink: 'video-playlists' }, | 77 | { label: $localize`PLAYLISTS`, routerLink: 'video-playlists' } |
71 | { label: $localize`ABOUT`, routerLink: 'about' } | ||
72 | ] | 78 | ] |
73 | } | 79 | } |
74 | 80 | ||
@@ -79,7 +85,7 @@ export class VideoChannelsComponent implements OnInit, OnDestroy { | |||
79 | if (this.isUserLoggedIn()) this.hotkeysService.remove(this.hotkeys) | 85 | if (this.isUserLoggedIn()) this.hotkeysService.remove(this.hotkeys) |
80 | } | 86 | } |
81 | 87 | ||
82 | get isInSmallView () { | 88 | isInSmallView () { |
83 | return this.screenService.isInSmallView() | 89 | return this.screenService.isInSmallView() |
84 | } | 90 | } |
85 | 91 | ||
@@ -87,12 +93,36 @@ export class VideoChannelsComponent implements OnInit, OnDestroy { | |||
87 | return this.authService.isLoggedIn() | 93 | return this.authService.isLoggedIn() |
88 | } | 94 | } |
89 | 95 | ||
90 | get isManageable () { | 96 | isManageable () { |
91 | if (!this.isUserLoggedIn()) return false | 97 | if (!this.isUserLoggedIn()) return false |
92 | return this.videoChannel.ownerAccount.userId === this.authService.getUser().id | 98 | |
99 | return this.videoChannel?.ownerAccount.userId === this.authService.getUser().id | ||
93 | } | 100 | } |
94 | 101 | ||
95 | activateCopiedMessage () { | 102 | activateCopiedMessage () { |
96 | this.notifier.success($localize`Username copied`) | 103 | this.notifier.success($localize`Username copied`) |
97 | } | 104 | } |
105 | |||
106 | hasShowMoreDescription () { | ||
107 | return !this.channelDescriptionExpanded && this.channelDescriptionHTML.length > 100 | ||
108 | } | ||
109 | |||
110 | showSupportModal () { | ||
111 | this.supportModal.show() | ||
112 | } | ||
113 | |||
114 | getAccountUrl () { | ||
115 | return [ '/accounts', this.videoChannel.ownerBy ] | ||
116 | } | ||
117 | |||
118 | private loadChannelVideosCount () { | ||
119 | this.videoService.getVideoChannelVideos({ | ||
120 | videoChannel: this.videoChannel, | ||
121 | videoPagination: { | ||
122 | currentPage: 1, | ||
123 | itemsPerPage: 0 | ||
124 | }, | ||
125 | sort: '-publishedAt' | ||
126 | }).subscribe(res => this.channelVideosCount = res.total) | ||
127 | } | ||
98 | } | 128 | } |
diff --git a/client/src/app/+video-channels/video-channels.module.ts b/client/src/app/+video-channels/video-channels.module.ts index 05236ff85..408f86225 100644 --- a/client/src/app/+video-channels/video-channels.module.ts +++ b/client/src/app/+video-channels/video-channels.module.ts | |||
@@ -2,10 +2,10 @@ import { NgModule } from '@angular/core' | |||
2 | import { SharedFormModule } from '@app/shared/shared-forms' | 2 | import { SharedFormModule } from '@app/shared/shared-forms' |
3 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' | 3 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' |
4 | import { SharedMainModule } from '@app/shared/shared-main' | 4 | import { SharedMainModule } from '@app/shared/shared-main' |
5 | import { SharedSupportModal } from '@app/shared/shared-support-modal' | ||
5 | import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription' | 6 | import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription' |
6 | import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature' | 7 | import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature' |
7 | import { SharedVideoPlaylistModule } from '@app/shared/shared-video-playlist' | 8 | import { SharedVideoPlaylistModule } from '@app/shared/shared-video-playlist' |
8 | import { VideoChannelAboutComponent } from './video-channel-about/video-channel-about.component' | ||
9 | import { VideoChannelPlaylistsComponent } from './video-channel-playlists/video-channel-playlists.component' | 9 | import { VideoChannelPlaylistsComponent } from './video-channel-playlists/video-channel-playlists.component' |
10 | import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component' | 10 | import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component' |
11 | import { VideoChannelsRoutingModule } from './video-channels-routing.module' | 11 | import { VideoChannelsRoutingModule } from './video-channels-routing.module' |
@@ -20,13 +20,13 @@ import { VideoChannelsComponent } from './video-channels.component' | |||
20 | SharedVideoPlaylistModule, | 20 | SharedVideoPlaylistModule, |
21 | SharedVideoMiniatureModule, | 21 | SharedVideoMiniatureModule, |
22 | SharedUserSubscriptionModule, | 22 | SharedUserSubscriptionModule, |
23 | SharedGlobalIconModule | 23 | SharedGlobalIconModule, |
24 | SharedSupportModal | ||
24 | ], | 25 | ], |
25 | 26 | ||
26 | declarations: [ | 27 | declarations: [ |
27 | VideoChannelsComponent, | 28 | VideoChannelsComponent, |
28 | VideoChannelVideosComponent, | 29 | VideoChannelVideosComponent, |
29 | VideoChannelAboutComponent, | ||
30 | VideoChannelPlaylistsComponent | 30 | VideoChannelPlaylistsComponent |
31 | ], | 31 | ], |
32 | 32 | ||
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts index 8780ca567..8e035b6bb 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | 1 | ||
2 | import { forkJoin } from 'rxjs' | 2 | import { forkJoin } from 'rxjs' |
3 | import { Component, EventEmitter, OnInit, Output } from '@angular/core' | 3 | import { AfterViewChecked, AfterViewInit, Component, EventEmitter, OnInit, Output } from '@angular/core' |
4 | import { Router } from '@angular/router' | 4 | import { Router } from '@angular/router' |
5 | import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core' | 5 | import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core' |
6 | import { scrollToTop } from '@app/helpers' | 6 | import { scrollToTop } from '@app/helpers' |
7 | import { FormValidatorService } from '@app/shared/shared-forms' | 7 | import { FormValidatorService } from '@app/shared/shared-forms' |
8 | import { VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' | 8 | import { VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' |
@@ -19,7 +19,7 @@ import { VideoSend } from './video-send' | |||
19 | './video-send.scss' | 19 | './video-send.scss' |
20 | ] | 20 | ] |
21 | }) | 21 | }) |
22 | export class VideoGoLiveComponent extends VideoSend implements OnInit, CanComponentDeactivate { | 22 | export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterViewInit, CanComponentDeactivate { |
23 | @Output() firstStepDone = new EventEmitter<string>() | 23 | @Output() firstStepDone = new EventEmitter<string>() |
24 | @Output() firstStepError = new EventEmitter<void>() | 24 | @Output() firstStepError = new EventEmitter<void>() |
25 | 25 | ||
@@ -41,7 +41,8 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, CanCompon | |||
41 | protected videoService: VideoService, | 41 | protected videoService: VideoService, |
42 | protected videoCaptionService: VideoCaptionService, | 42 | protected videoCaptionService: VideoCaptionService, |
43 | private liveVideoService: LiveVideoService, | 43 | private liveVideoService: LiveVideoService, |
44 | private router: Router | 44 | private router: Router, |
45 | private hooks: HooksService | ||
45 | ) { | 46 | ) { |
46 | super() | 47 | super() |
47 | } | 48 | } |
@@ -50,6 +51,10 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, CanCompon | |||
50 | super.ngOnInit() | 51 | super.ngOnInit() |
51 | } | 52 | } |
52 | 53 | ||
54 | ngAfterViewInit () { | ||
55 | this.hooks.runAction('action:go-live.init', 'video-edit') | ||
56 | } | ||
57 | |||
53 | canDeactivate () { | 58 | canDeactivate () { |
54 | return { canDeactivate: true } | 59 | return { canDeactivate: true } |
55 | } | 60 | } |
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.scss b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.scss index 1fef74994..dd87641fc 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.scss +++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.scss | |||
@@ -3,8 +3,8 @@ | |||
3 | 3 | ||
4 | .first-step-block { | 4 | .first-step-block { |
5 | .torrent-or-magnet { | 5 | .torrent-or-magnet { |
6 | @include divider($color: pvar(--inputPlaceholderColor), $background: pvar(--submenuColor)); | 6 | @include divider($color: pvar(--inputPlaceholderColor), $background: pvar(--submenuBackgroundColor)); |
7 | 7 | ||
8 | &[data-content] { | 8 | &[data-content] { |
9 | margin: 1.5rem 0; | 9 | margin: 1.5rem 0; |
10 | } | 10 | } |
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts index 01087e525..3aae24732 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { Component, ElementRef, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { AfterViewInit, Component, ElementRef, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { Router } from '@angular/router' | 2 | import { Router } from '@angular/router' |
3 | import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core' | 3 | import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core' |
4 | import { scrollToTop } from '@app/helpers' | 4 | import { scrollToTop } from '@app/helpers' |
5 | import { FormValidatorService } from '@app/shared/shared-forms' | 5 | import { FormValidatorService } from '@app/shared/shared-forms' |
6 | import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' | 6 | import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' |
@@ -18,7 +18,7 @@ import { VideoSend } from './video-send' | |||
18 | './video-send.scss' | 18 | './video-send.scss' |
19 | ] | 19 | ] |
20 | }) | 20 | }) |
21 | export class VideoImportTorrentComponent extends VideoSend implements OnInit, CanComponentDeactivate { | 21 | export class VideoImportTorrentComponent extends VideoSend implements OnInit, AfterViewInit, CanComponentDeactivate { |
22 | @Output() firstStepDone = new EventEmitter<string>() | 22 | @Output() firstStepDone = new EventEmitter<string>() |
23 | @Output() firstStepError = new EventEmitter<void>() | 23 | @Output() firstStepError = new EventEmitter<void>() |
24 | @ViewChild('torrentfileInput') torrentfileInput: ElementRef<HTMLInputElement> | 24 | @ViewChild('torrentfileInput') torrentfileInput: ElementRef<HTMLInputElement> |
@@ -43,7 +43,8 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca | |||
43 | protected videoService: VideoService, | 43 | protected videoService: VideoService, |
44 | protected videoCaptionService: VideoCaptionService, | 44 | protected videoCaptionService: VideoCaptionService, |
45 | private router: Router, | 45 | private router: Router, |
46 | private videoImportService: VideoImportService | 46 | private videoImportService: VideoImportService, |
47 | private hooks: HooksService | ||
47 | ) { | 48 | ) { |
48 | super() | 49 | super() |
49 | } | 50 | } |
@@ -52,6 +53,10 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca | |||
52 | super.ngOnInit() | 53 | super.ngOnInit() |
53 | } | 54 | } |
54 | 55 | ||
56 | ngAfterViewInit () { | ||
57 | this.hooks.runAction('action:video-torrent-import.init', 'video-edit') | ||
58 | } | ||
59 | |||
55 | canDeactivate () { | 60 | canDeactivate () { |
56 | return { canDeactivate: true } | 61 | return { canDeactivate: true } |
57 | } | 62 | } |
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts index c447c179d..7a9fe369f 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { map, switchMap } from 'rxjs/operators' | 1 | import { map, switchMap } from 'rxjs/operators' |
2 | import { Component, EventEmitter, OnInit, Output } from '@angular/core' | 2 | import { AfterViewInit, Component, EventEmitter, OnInit, Output } from '@angular/core' |
3 | import { Router } from '@angular/router' | 3 | import { Router } from '@angular/router' |
4 | import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core' | 4 | import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService } from '@app/core' |
5 | import { getAbsoluteAPIUrl, scrollToTop } from '@app/helpers' | 5 | import { getAbsoluteAPIUrl, scrollToTop } from '@app/helpers' |
6 | import { FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormValidatorService } from '@app/shared/shared-forms' |
7 | import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' | 7 | import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' |
@@ -18,7 +18,7 @@ import { VideoSend } from './video-send' | |||
18 | './video-send.scss' | 18 | './video-send.scss' |
19 | ] | 19 | ] |
20 | }) | 20 | }) |
21 | export class VideoImportUrlComponent extends VideoSend implements OnInit, CanComponentDeactivate { | 21 | export class VideoImportUrlComponent extends VideoSend implements OnInit, AfterViewInit, CanComponentDeactivate { |
22 | @Output() firstStepDone = new EventEmitter<string>() | 22 | @Output() firstStepDone = new EventEmitter<string>() |
23 | @Output() firstStepError = new EventEmitter<void>() | 23 | @Output() firstStepError = new EventEmitter<void>() |
24 | 24 | ||
@@ -42,8 +42,9 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom | |||
42 | protected videoService: VideoService, | 42 | protected videoService: VideoService, |
43 | protected videoCaptionService: VideoCaptionService, | 43 | protected videoCaptionService: VideoCaptionService, |
44 | private router: Router, | 44 | private router: Router, |
45 | private videoImportService: VideoImportService | 45 | private videoImportService: VideoImportService, |
46 | ) { | 46 | private hooks: HooksService |
47 | ) { | ||
47 | super() | 48 | super() |
48 | } | 49 | } |
49 | 50 | ||
@@ -51,6 +52,10 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom | |||
51 | super.ngOnInit() | 52 | super.ngOnInit() |
52 | } | 53 | } |
53 | 54 | ||
55 | ngAfterViewInit () { | ||
56 | this.hooks.runAction('action:video-url-import.init', 'video-edit') | ||
57 | } | ||
58 | |||
54 | canDeactivate () { | 59 | canDeactivate () { |
55 | return { canDeactivate: true } | 60 | return { canDeactivate: true } |
56 | } | 61 | } |
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts index ca21b61cd..effb37077 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts | |||
@@ -1,15 +1,15 @@ | |||
1 | import { Subscription } from 'rxjs' | 1 | import { Subscription } from 'rxjs' |
2 | import { HttpErrorResponse, HttpEventType, HttpResponse } from '@angular/common/http' | 2 | import { HttpErrorResponse, HttpEventType, HttpResponse } from '@angular/common/http' |
3 | import { Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' | 3 | import { AfterViewInit, Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' |
4 | import { Router } from '@angular/router' | 4 | import { Router } from '@angular/router' |
5 | import { AuthService, CanComponentDeactivate, Notifier, ServerService, UserService } from '@app/core' | 5 | import { AuthService, CanComponentDeactivate, HooksService, Notifier, ServerService, UserService } from '@app/core' |
6 | import { scrollToTop, uploadErrorHandler } from '@app/helpers' | 6 | import { scrollToTop, uploadErrorHandler } from '@app/helpers' |
7 | import { FormValidatorService } from '@app/shared/shared-forms' | 7 | import { FormValidatorService } from '@app/shared/shared-forms' |
8 | import { BytesPipe, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' | 8 | import { BytesPipe, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' |
9 | import { LoadingBarService } from '@ngx-loading-bar/core' | 9 | import { LoadingBarService } from '@ngx-loading-bar/core' |
10 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | ||
10 | import { VideoPrivacy } from '@shared/models' | 11 | import { VideoPrivacy } from '@shared/models' |
11 | import { VideoSend } from './video-send' | 12 | import { VideoSend } from './video-send' |
12 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | ||
13 | 13 | ||
14 | @Component({ | 14 | @Component({ |
15 | selector: 'my-video-upload', | 15 | selector: 'my-video-upload', |
@@ -20,7 +20,7 @@ import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | |||
20 | './video-send.scss' | 20 | './video-send.scss' |
21 | ] | 21 | ] |
22 | }) | 22 | }) |
23 | export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy, CanComponentDeactivate { | 23 | export class VideoUploadComponent extends VideoSend implements OnInit, AfterViewInit, OnDestroy, CanComponentDeactivate { |
24 | @Output() firstStepDone = new EventEmitter<string>() | 24 | @Output() firstStepDone = new EventEmitter<string>() |
25 | @Output() firstStepError = new EventEmitter<void>() | 25 | @Output() firstStepError = new EventEmitter<void>() |
26 | @ViewChild('videofileInput') videofileInput: ElementRef<HTMLInputElement> | 26 | @ViewChild('videofileInput') videofileInput: ElementRef<HTMLInputElement> |
@@ -60,7 +60,8 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy | |||
60 | protected videoService: VideoService, | 60 | protected videoService: VideoService, |
61 | protected videoCaptionService: VideoCaptionService, | 61 | protected videoCaptionService: VideoCaptionService, |
62 | private userService: UserService, | 62 | private userService: UserService, |
63 | private router: Router | 63 | private router: Router, |
64 | private hooks: HooksService | ||
64 | ) { | 65 | ) { |
65 | super() | 66 | super() |
66 | } | 67 | } |
@@ -79,6 +80,10 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy | |||
79 | }) | 80 | }) |
80 | } | 81 | } |
81 | 82 | ||
83 | ngAfterViewInit () { | ||
84 | this.hooks.runAction('action:video-upload.init', 'video-edit') | ||
85 | } | ||
86 | |||
82 | ngOnDestroy () { | 87 | ngOnDestroy () { |
83 | if (this.videoUploadObservable) this.videoUploadObservable.unsubscribe() | 88 | if (this.videoUploadObservable) this.videoUploadObservable.unsubscribe() |
84 | } | 89 | } |
diff --git a/client/src/app/+videos/+video-edit/video-add.component.scss b/client/src/app/+videos/+video-edit/video-add.component.scss index 5db9e823d..1ebee946b 100644 --- a/client/src/app/+videos/+video-edit/video-add.component.scss +++ b/client/src/app/+videos/+video-edit/video-add.component.scss | |||
@@ -67,7 +67,7 @@ $nav-link-height: 40px; | |||
67 | &.active { | 67 | &.active { |
68 | border-color: $border-color; | 68 | border-color: $border-color; |
69 | border-bottom-color: transparent; | 69 | border-bottom-color: transparent; |
70 | background-color: pvar(--submenuColor) !important; | 70 | background-color: pvar(--submenuBackgroundColor) !important; |
71 | 71 | ||
72 | span { | 72 | span { |
73 | border-bottom-color: pvar(--mainColor); | 73 | border-bottom-color: pvar(--mainColor); |
@@ -84,7 +84,7 @@ $nav-link-height: 40px; | |||
84 | border: $border-width $border-type $border-color; | 84 | border: $border-width $border-type $border-color; |
85 | border-top: transparent; | 85 | border-top: transparent; |
86 | 86 | ||
87 | background-color: pvar(--submenuColor); | 87 | background-color: pvar(--submenuBackgroundColor); |
88 | border-bottom-left-radius: 3px; | 88 | border-bottom-left-radius: 3px; |
89 | border-bottom-right-radius: 3px; | 89 | border-bottom-right-radius: 3px; |
90 | width: 100%; | 90 | width: 100%; |
diff --git a/client/src/app/+videos/+video-watch/comment/video-comments.component.html b/client/src/app/+videos/+video-watch/comment/video-comments.component.html index 4a6426d30..9e6fde2e0 100644 --- a/client/src/app/+videos/+video-watch/comment/video-comments.component.html +++ b/client/src/app/+videos/+video-watch/comment/video-comments.component.html | |||
@@ -1,12 +1,7 @@ | |||
1 | <div> | 1 | <div> |
2 | <div class="title-block"> | 2 | <div class="title-block"> |
3 | <h2 class="title-page title-page-single"> | 3 | <h2 class="title-page title-page-single"> |
4 | <ng-container *ngIf="totalNotDeletedComments > 0; then hasComments; else noComments"></ng-container> | 4 | {totalNotDeletedComments, plural, =0 {Comments} =1 {1 Comment} other {{{totalNotDeletedComments}} Comments}} |
5 | <ng-template #hasComments> | ||
6 | <ng-container i18n *ngIf="totalNotDeletedComments === 1; else manyComments">1 Comment</ng-container> | ||
7 | <ng-template i18n #manyComments>{{ totalNotDeletedComments }} Comments</ng-template> | ||
8 | </ng-template> | ||
9 | <ng-template i18n #noComments>Comments</ng-template> | ||
10 | </h2> | 5 | </h2> |
11 | 6 | ||
12 | <my-feed [syndicationItems]="syndicationItems"></my-feed> | 7 | <my-feed [syndicationItems]="syndicationItems"></my-feed> |
@@ -79,15 +74,17 @@ | |||
79 | <span class="glyphicon glyphicon-menu-down"></span> | 74 | <span class="glyphicon glyphicon-menu-down"></span> |
80 | 75 | ||
81 | <ng-container *ngIf="comment.totalRepliesFromVideoAuthor > 0; then hasAuthorComments; else noAuthorComments"></ng-container> | 76 | <ng-container *ngIf="comment.totalRepliesFromVideoAuthor > 0; then hasAuthorComments; else noAuthorComments"></ng-container> |
77 | |||
82 | <ng-template #hasAuthorComments> | 78 | <ng-template #hasAuthorComments> |
83 | <ng-container *ngIf="comment.totalReplies !== comment.totalRepliesFromVideoAuthor; else onlyAuthorComments" i18n> | 79 | <ng-container *ngIf="comment.totalReplies !== comment.totalRepliesFromVideoAuthor; else onlyAuthorComments" i18n> |
84 | View {{ comment.totalReplies }} replies from {{ video?.account?.displayName || 'the author' }} and others | 80 | View {comment.totalReplies, plural, =1 {1 reply} other {{{ comment.totalReplies }} replies}} from {{ video?.account?.displayName || 'the author' }} and others |
85 | </ng-container> | 81 | </ng-container> |
86 | <ng-template i18n #onlyAuthorComments> | 82 | <ng-template i18n #onlyAuthorComments> |
87 | View {{ comment.totalReplies }} replies from {{ video?.account?.displayName || 'the author' }} | 83 | View {comment.totalReplies, plural, =1 {1 reply} other {{{ comment.totalReplies }} replies}} from {{ video?.account?.displayName || 'the author' }} |
88 | </ng-template> | 84 | </ng-template> |
89 | </ng-template> | 85 | </ng-template> |
90 | <ng-template i18n #noAuthorComments>View {{ comment.totalReplies }} replies</ng-template> | 86 | |
87 | <ng-template i18n #noAuthorComments>View {comment.totalReplies, plural, =1 {1 reply} other {{{ comment.totalReplies }} replies}}</ng-template> | ||
91 | 88 | ||
92 | <my-small-loader class="comment-thread-loading ml-1" [loading]="threadLoading[comment.id]"></my-small-loader> | 89 | <my-small-loader class="comment-thread-loading ml-1" [loading]="threadLoading[comment.id]"></my-small-loader> |
93 | </div> | 90 | </div> |
diff --git a/client/src/app/+videos/+video-watch/comment/video-comments.component.ts b/client/src/app/+videos/+video-watch/comment/video-comments.component.ts index d36dd9e34..210236b61 100644 --- a/client/src/app/+videos/+video-watch/comment/video-comments.component.ts +++ b/client/src/app/+videos/+video-watch/comment/video-comments.component.ts | |||
@@ -5,7 +5,6 @@ import { AuthService, ComponentPagination, ConfirmService, hasMoreItems, Notifie | |||
5 | import { HooksService } from '@app/core/plugins/hooks.service' | 5 | import { HooksService } from '@app/core/plugins/hooks.service' |
6 | import { Syndication, VideoDetails } from '@app/shared/shared-main' | 6 | import { Syndication, VideoDetails } from '@app/shared/shared-main' |
7 | import { VideoComment, VideoCommentService, VideoCommentThreadTree } from '@app/shared/shared-video-comment' | 7 | import { VideoComment, VideoCommentService, VideoCommentThreadTree } from '@app/shared/shared-video-comment' |
8 | import { ThisReceiver } from '@angular/compiler' | ||
9 | 8 | ||
10 | @Component({ | 9 | @Component({ |
11 | selector: 'my-video-comments', | 10 | selector: 'my-video-comments', |
diff --git a/client/src/app/+videos/+video-watch/modal/video-support.component.ts b/client/src/app/+videos/+video-watch/modal/video-support.component.ts deleted file mode 100644 index bd5290a72..000000000 --- a/client/src/app/+videos/+video-watch/modal/video-support.component.ts +++ /dev/null | |||
@@ -1,31 +0,0 @@ | |||
1 | import { Component, Input, ViewChild } from '@angular/core' | ||
2 | import { MarkdownService } from '@app/core' | ||
3 | import { VideoDetails } from '@app/shared/shared-main' | ||
4 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | ||
5 | |||
6 | @Component({ | ||
7 | selector: 'my-video-support', | ||
8 | templateUrl: './video-support.component.html', | ||
9 | styleUrls: [ './video-support.component.scss' ] | ||
10 | }) | ||
11 | export class VideoSupportComponent { | ||
12 | @Input() video: VideoDetails = null | ||
13 | |||
14 | @ViewChild('modal', { static: true }) modal: NgbModal | ||
15 | |||
16 | videoHTMLSupport = '' | ||
17 | |||
18 | constructor ( | ||
19 | private markdownService: MarkdownService, | ||
20 | private modalService: NgbModal | ||
21 | ) { } | ||
22 | |||
23 | show () { | ||
24 | const modalRef = this.modalService.open(this.modal, { centered: true }) | ||
25 | |||
26 | this.markdownService.enhancedMarkdownToHTML(this.video.support) | ||
27 | .then(r => this.videoHTMLSupport = r) | ||
28 | |||
29 | return modalRef | ||
30 | } | ||
31 | } | ||
diff --git a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html index 3c7c679b8..e0e9f92e7 100644 --- a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html +++ b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.html | |||
@@ -1,4 +1,4 @@ | |||
1 | <div class="other-videos"> | 1 | <div class="other-videos" [ngClass]="{ 'display-as-row': displayAsRow }"> |
2 | <ng-container *ngIf="hasVideos$ | async"> | 2 | <ng-container *ngIf="hasVideos$ | async"> |
3 | <div class="title-page-container"> | 3 | <div class="title-page-container"> |
4 | <h2 i18n class="title-page title-page-single"> | 4 | <h2 i18n class="title-page title-page-single"> |
@@ -14,7 +14,7 @@ | |||
14 | 14 | ||
15 | <ng-container *ngFor="let video of (videos$ | async); let i = index; let length = count"> | 15 | <ng-container *ngFor="let video of (videos$ | async); let i = index; let length = count"> |
16 | <my-video-miniature | 16 | <my-video-miniature |
17 | [displayOptions]="displayOptions" [video]="video" [user]="userMiniature" | 17 | [displayOptions]="displayOptions" [video]="video" [user]="userMiniature" [displayAsRow]="displayAsRow" |
18 | (videoBlocked)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()" (videoAccountMuted)="onVideoRemoved()"> | 18 | (videoBlocked)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()" (videoAccountMuted)="onVideoRemoved()"> |
19 | </my-video-miniature> | 19 | </my-video-miniature> |
20 | 20 | ||
diff --git a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss index b278c9654..c9fae6f27 100644 --- a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss +++ b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.scss | |||
@@ -1,3 +1,6 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
1 | .title-page-container { | 4 | .title-page-container { |
2 | display: flex; | 5 | display: flex; |
3 | justify-content: space-between; | 6 | justify-content: space-between; |
@@ -11,6 +14,10 @@ | |||
11 | } | 14 | } |
12 | } | 15 | } |
13 | 16 | ||
17 | .title-page { | ||
18 | margin-top: 0; | ||
19 | } | ||
20 | |||
14 | .title-page-autoplay { | 21 | .title-page-autoplay { |
15 | display: flex; | 22 | display: flex; |
16 | width: max-content; | 23 | width: max-content; |
@@ -29,3 +36,29 @@ | |||
29 | hr { | 36 | hr { |
30 | margin-top: 0; | 37 | margin-top: 0; |
31 | } | 38 | } |
39 | |||
40 | my-video-miniature { | ||
41 | display: block; | ||
42 | } | ||
43 | |||
44 | .other-videos:not(.display-as-row) my-video-miniature { | ||
45 | min-width: $video-thumbnail-medium-width; | ||
46 | max-width: $video-thumbnail-medium-width; | ||
47 | } | ||
48 | |||
49 | .display-as-row { | ||
50 | my-video-miniature { | ||
51 | margin-bottom: 20px; | ||
52 | } | ||
53 | |||
54 | hr { | ||
55 | display: none; | ||
56 | } | ||
57 | |||
58 | @media screen and (max-width: $mobile-view) { | ||
59 | my-video-miniature { | ||
60 | margin-bottom: 10px; | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
diff --git a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts index a1c8e0661..89b9c01b6 100644 --- a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts +++ b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts | |||
@@ -16,6 +16,8 @@ import { RecommendedVideosStore } from './recommended-videos.store' | |||
16 | export class RecommendedVideosComponent implements OnInit, OnChanges { | 16 | export class RecommendedVideosComponent implements OnInit, OnChanges { |
17 | @Input() inputRecommendation: RecommendationInfo | 17 | @Input() inputRecommendation: RecommendationInfo |
18 | @Input() playlist: VideoPlaylist | 18 | @Input() playlist: VideoPlaylist |
19 | @Input() displayAsRow: boolean | ||
20 | |||
19 | @Output() gotRecommendations = new EventEmitter<Video[]>() | 21 | @Output() gotRecommendations = new EventEmitter<Video[]>() |
20 | 22 | ||
21 | autoPlayNextVideo: boolean | 23 | autoPlayNextVideo: boolean |
diff --git a/client/src/app/shared/shared-main/account/video-avatar-channel.component.html b/client/src/app/+videos/+video-watch/video-avatar-channel.component.html index 310cc926f..5058f05dd 100644 --- a/client/src/app/shared/shared-main/account/video-avatar-channel.component.html +++ b/client/src/app/+videos/+video-watch/video-avatar-channel.component.html | |||
@@ -1,8 +1,9 @@ | |||
1 | <div class="wrapper" [ngClass]="'avatar-' + size"> | 1 | <div class="wrapper" [ngClass]="'avatar-' + size"> |
2 | <ng-container *ngIf="!isChannelAvatarNull() && !genericChannel"> | 2 | <ng-container *ngIf="!isChannelAvatarNull() && !genericChannel"> |
3 | <a [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle"> | 3 | <a [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle"> |
4 | <img [src]="video.videoChannelAvatarUrl" i18n-alt alt="Channel avatar" /> | 4 | <img [src]="video.videoChannelAvatarUrl" i18n-alt alt="Channel avatar" class="channel-avatar" /> |
5 | </a> | 5 | </a> |
6 | |||
6 | <a [routerLink]="[ '/accounts', video.byAccount ]" [title]="accountLinkTitle"> | 7 | <a [routerLink]="[ '/accounts', video.byAccount ]" [title]="accountLinkTitle"> |
7 | <img [src]="video.accountAvatarUrl" i18n-alt alt="Account avatar" /> | 8 | <img [src]="video.accountAvatarUrl" i18n-alt alt="Account avatar" /> |
8 | </a> | 9 | </a> |
@@ -14,7 +15,7 @@ | |||
14 | </a> | 15 | </a> |
15 | 16 | ||
16 | <a [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle"> | 17 | <a [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle"> |
17 | <img [src]="video.videoChannelAvatarUrl" i18n-alt alt="Channel avatar" /> | 18 | <img [src]="video.videoChannelAvatarUrl" i18n-alt alt="Channel avatar" class="channel-avatar" /> |
18 | </a> | 19 | </a> |
19 | </ng-container> | 20 | </ng-container> |
20 | 21 | ||
diff --git a/client/src/app/shared/shared-main/account/video-avatar-channel.component.scss b/client/src/app/+videos/+video-watch/video-avatar-channel.component.scss index 37709fce6..4998e85fa 100644 --- a/client/src/app/shared/shared-main/account/video-avatar-channel.component.scss +++ b/client/src/app/+videos/+video-watch/video-avatar-channel.component.scss | |||
@@ -25,8 +25,12 @@ | |||
25 | position: absolute; | 25 | position: absolute; |
26 | top:50%; | 26 | top:50%; |
27 | left:50%; | 27 | left:50%; |
28 | border-radius: 50%; | 28 | transform: translate(-50%,-50%); |
29 | transform: translate(-50%,-50%) | 29 | border-radius: 5px; |
30 | |||
31 | &:not(.channel-avatar) { | ||
32 | border-radius: 50%; | ||
33 | } | ||
30 | } | 34 | } |
31 | 35 | ||
32 | a:nth-of-type(2) img { | 36 | a:nth-of-type(2) img { |
diff --git a/client/src/app/shared/shared-main/account/video-avatar-channel.component.ts b/client/src/app/+videos/+video-watch/video-avatar-channel.component.ts index 440e2b522..0b6e796df 100644 --- a/client/src/app/shared/shared-main/account/video-avatar-channel.component.ts +++ b/client/src/app/+videos/+video-watch/video-avatar-channel.component.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { Component, Input, OnInit } from '@angular/core' | 1 | import { Component, Input, OnInit } from '@angular/core' |
2 | import { Video } from '../video/video.model' | 2 | import { Video } from '@app/shared/shared-main/video' |
3 | 3 | ||
4 | @Component({ | 4 | @Component({ |
5 | selector: 'my-video-avatar-channel', | 5 | selector: 'my-video-avatar-channel', |
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.html b/client/src/app/+videos/+video-watch/video-watch.component.html index b17f898ce..99103c2c3 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.html +++ b/client/src/app/+videos/+video-watch/video-watch.component.html | |||
@@ -142,7 +142,7 @@ | |||
142 | <ng-container *ngIf="isUserLoggedIn()"> | 142 | <ng-container *ngIf="isUserLoggedIn()"> |
143 | <my-video-actions-dropdown | 143 | <my-video-actions-dropdown |
144 | placement="bottom auto" buttonDirection="horizontal" [buttonStyled]="true" [video]="video" [videoCaptions]="videoCaptions" | 144 | placement="bottom auto" buttonDirection="horizontal" [buttonStyled]="true" [video]="video" [videoCaptions]="videoCaptions" |
145 | [displayOptions]="videoActionsOptions" (videoRemoved)="onVideoRemoved()" (modalOpened)="onModalOpened()" | 145 | [displayOptions]="videoActionsOptions" (videoRemoved)="onVideoRemoved()" |
146 | ></my-video-actions-dropdown> | 146 | ></my-video-actions-dropdown> |
147 | </ng-container> | 147 | </ng-container> |
148 | </div> | 148 | </div> |
@@ -289,6 +289,7 @@ | |||
289 | </div> | 289 | </div> |
290 | 290 | ||
291 | <my-recommended-videos | 291 | <my-recommended-videos |
292 | [displayAsRow]="displayOtherVideosAsRow()" | ||
292 | [inputRecommendation]="{ uuid: video.uuid, tags: video.tags }" | 293 | [inputRecommendation]="{ uuid: video.uuid, tags: video.tags }" |
293 | [playlist]="playlist" | 294 | [playlist]="playlist" |
294 | (gotRecommendations)="onRecommendations($event)" | 295 | (gotRecommendations)="onRecommendations($event)" |
@@ -313,6 +314,6 @@ | |||
313 | </div> | 314 | </div> |
314 | 315 | ||
315 | <ng-container *ngIf="video !== null"> | 316 | <ng-container *ngIf="video !== null"> |
316 | <my-video-support #videoSupportModal [video]="video"></my-video-support> | 317 | <my-support-modal #supportModal [video]="video"></my-support-modal> |
317 | <my-video-share #videoShareModal [video]="video" [videoCaptions]="videoCaptions" [playlist]="playlist"></my-video-share> | 318 | <my-video-share #videoShareModal [video]="video" [videoCaptions]="videoCaptions" [playlist]="playlist"></my-video-share> |
318 | </ng-container> | 319 | </ng-container> |
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.scss b/client/src/app/+videos/+video-watch/video-watch.component.scss index 555126cbc..2e566e3fb 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.scss +++ b/client/src/app/+videos/+video-watch/video-watch.component.scss | |||
@@ -3,7 +3,7 @@ | |||
3 | @import '_bootstrap-variables'; | 3 | @import '_bootstrap-variables'; |
4 | @import '_miniature'; | 4 | @import '_miniature'; |
5 | 5 | ||
6 | $player-factor: 1.7; // 16/9 | 6 | $player-factor: #{16/9}; |
7 | $video-info-margin-left: 44px; | 7 | $video-info-margin-left: 44px; |
8 | 8 | ||
9 | @function getPlayerHeight($width){ | 9 | @function getPlayerHeight($width){ |
@@ -179,12 +179,6 @@ $video-info-margin-left: 44px; | |||
179 | &:hover { | 179 | &:hover { |
180 | opacity: 0.8; | 180 | opacity: 0.8; |
181 | } | 181 | } |
182 | |||
183 | img { | ||
184 | @include avatar(18px); | ||
185 | |||
186 | margin: -2px 5px 0 0; | ||
187 | } | ||
188 | } | 182 | } |
189 | 183 | ||
190 | .video-info-channel-left { | 184 | .video-info-channel-left { |
@@ -413,37 +407,12 @@ $video-info-margin-left: 44px; | |||
413 | } | 407 | } |
414 | } | 408 | } |
415 | } | 409 | } |
410 | } | ||
416 | 411 | ||
417 | ::ng-deep .other-videos { | 412 | my-recommended-videos { |
418 | padding-left: 15px; | 413 | display: block; |
419 | min-width: $video-miniature-width; | 414 | padding-left: 15px; |
420 | 415 | min-width: 250px; | |
421 | @media screen and (min-width: 1800px - (3* $video-miniature-width)) { | ||
422 | width: min-content; | ||
423 | } | ||
424 | |||
425 | .title-page { | ||
426 | margin: 0 !important; | ||
427 | } | ||
428 | |||
429 | .video-miniature { | ||
430 | display: flex; | ||
431 | width: max-content; | ||
432 | height: 100%; | ||
433 | padding-bottom: 20px; | ||
434 | flex-wrap: wrap; | ||
435 | } | ||
436 | |||
437 | .video-bottom { | ||
438 | @media screen and (max-width: 1800px - (3* $video-miniature-width)) { | ||
439 | margin-left: 1rem; | ||
440 | } | ||
441 | @media screen and (max-width: 500px) { | ||
442 | margin-left: 0; | ||
443 | margin-top: .5rem; | ||
444 | } | ||
445 | } | ||
446 | } | ||
447 | } | 416 | } |
448 | 417 | ||
449 | my-video-comments { | 418 | my-video-comments { |
@@ -537,6 +506,7 @@ my-video-comments { | |||
537 | } | 506 | } |
538 | } | 507 | } |
539 | 508 | ||
509 | // Use the same breakpoint than in the typescript component to display the other video miniatures as row | ||
540 | @media screen and (max-width: 1100px) { | 510 | @media screen and (max-width: 1100px) { |
541 | #video-wrapper { | 511 | #video-wrapper { |
542 | flex-direction: column; | 512 | flex-direction: column; |
@@ -549,15 +519,10 @@ my-video-comments { | |||
549 | 519 | ||
550 | .video-bottom { | 520 | .video-bottom { |
551 | flex-direction: column; | 521 | flex-direction: column; |
522 | } | ||
552 | 523 | ||
553 | ::ng-deep .other-videos { | 524 | my-recommended-videos { |
554 | padding-left: 0 !important; | 525 | padding-left: 0; |
555 | |||
556 | ::ng-deep .video-miniature { | ||
557 | flex-direction: row; | ||
558 | width: auto; | ||
559 | } | ||
560 | } | ||
561 | } | 526 | } |
562 | } | 527 | } |
563 | 528 | ||
@@ -579,10 +544,6 @@ my-video-comments { | |||
579 | } | 544 | } |
580 | } | 545 | } |
581 | 546 | ||
582 | ::ng-deep .other-videos .video-miniature { | ||
583 | flex-direction: column; | ||
584 | } | ||
585 | |||
586 | .privacy-concerns { | 547 | .privacy-concerns { |
587 | width: 100%; | 548 | width: 100%; |
588 | } | 549 | } |
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 7a98cab3b..de5fb4ed0 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts | |||
@@ -21,6 +21,7 @@ import { RedirectService } from '@app/core/routing/redirect.service' | |||
21 | import { isXPercentInViewport, scrollToTop } from '@app/helpers' | 21 | import { isXPercentInViewport, scrollToTop } from '@app/helpers' |
22 | import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' | 22 | import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' |
23 | import { VideoShareComponent } from '@app/shared/shared-share-modal' | 23 | import { VideoShareComponent } from '@app/shared/shared-share-modal' |
24 | import { SupportModalComponent } from '@app/shared/shared-support-modal' | ||
24 | import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' | 25 | import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' |
25 | import { VideoActionsDisplayType, VideoDownloadComponent } from '@app/shared/shared-video-miniature' | 26 | import { VideoActionsDisplayType, VideoDownloadComponent } from '@app/shared/shared-video-miniature' |
26 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' | 27 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' |
@@ -28,7 +29,12 @@ import { MetaService } from '@ngx-meta/core' | |||
28 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' | 29 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' |
29 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | 30 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' |
30 | import { ServerConfig, ServerErrorCode, UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '@shared/models' | 31 | import { ServerConfig, ServerErrorCode, UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '@shared/models' |
31 | import { getStoredP2PEnabled, getStoredTheater } from '../../../assets/player/peertube-player-local-storage' | 32 | import { |
33 | cleanupVideoWatch, | ||
34 | getStoredP2PEnabled, | ||
35 | getStoredTheater, | ||
36 | getStoredVideoWatchHistory | ||
37 | } from '../../../assets/player/peertube-player-local-storage' | ||
32 | import { | 38 | import { |
33 | CustomizationOptions, | 39 | CustomizationOptions, |
34 | P2PMediaLoaderOptions, | 40 | P2PMediaLoaderOptions, |
@@ -39,7 +45,6 @@ import { | |||
39 | } from '../../../assets/player/peertube-player-manager' | 45 | } from '../../../assets/player/peertube-player-manager' |
40 | import { isWebRTCDisabled, timeToInt } from '../../../assets/player/utils' | 46 | import { isWebRTCDisabled, timeToInt } from '../../../assets/player/utils' |
41 | import { environment } from '../../../environments/environment' | 47 | import { environment } from '../../../environments/environment' |
42 | import { VideoSupportComponent } from './modal/video-support.component' | ||
43 | import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' | 48 | import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' |
44 | 49 | ||
45 | type URLOptions = CustomizationOptions & { playerMode: PlayerMode } | 50 | type URLOptions = CustomizationOptions & { playerMode: PlayerMode } |
@@ -54,7 +59,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
54 | 59 | ||
55 | @ViewChild('videoWatchPlaylist', { static: true }) videoWatchPlaylist: VideoWatchPlaylistComponent | 60 | @ViewChild('videoWatchPlaylist', { static: true }) videoWatchPlaylist: VideoWatchPlaylistComponent |
56 | @ViewChild('videoShareModal') videoShareModal: VideoShareComponent | 61 | @ViewChild('videoShareModal') videoShareModal: VideoShareComponent |
57 | @ViewChild('videoSupportModal') videoSupportModal: VideoSupportComponent | 62 | @ViewChild('supportModal') supportModal: SupportModalComponent |
58 | @ViewChild('subscribeButton') subscribeButton: SubscribeButtonComponent | 63 | @ViewChild('subscribeButton') subscribeButton: SubscribeButtonComponent |
59 | @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent | 64 | @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent |
60 | 65 | ||
@@ -195,6 +200,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
195 | this.theaterEnabled = getStoredTheater() | 200 | this.theaterEnabled = getStoredTheater() |
196 | 201 | ||
197 | this.hooks.runAction('action:video-watch.init', 'video-watch') | 202 | this.hooks.runAction('action:video-watch.init', 'video-watch') |
203 | |||
204 | setTimeout(cleanupVideoWatch, 1500) // Run in timeout to ensure we're not blocking the UI | ||
198 | } | 205 | } |
199 | 206 | ||
200 | ngOnDestroy () { | 207 | ngOnDestroy () { |
@@ -277,23 +284,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
277 | } | 284 | } |
278 | 285 | ||
279 | showSupportModal () { | 286 | showSupportModal () { |
280 | // Check video was playing before opening support modal | 287 | this.supportModal.show() |
281 | const isVideoPlaying = this.isPlaying() | ||
282 | |||
283 | this.pausePlayer() | ||
284 | |||
285 | const modalRef = this.videoSupportModal.show() | ||
286 | |||
287 | modalRef.result.then(() => { | ||
288 | if (isVideoPlaying) { | ||
289 | this.resumePlayer() | ||
290 | } | ||
291 | }) | ||
292 | } | 288 | } |
293 | 289 | ||
294 | showShareModal () { | 290 | showShareModal () { |
295 | this.pausePlayer() | ||
296 | |||
297 | this.videoShareModal.show(this.currentTime, this.videoWatchPlaylist.currentPlaylistPosition) | 291 | this.videoShareModal.show(this.currentTime, this.videoWatchPlaylist.currentPlaylistPosition) |
298 | } | 292 | } |
299 | 293 | ||
@@ -316,10 +310,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
316 | } | 310 | } |
317 | } | 311 | } |
318 | 312 | ||
319 | onModalOpened () { | ||
320 | this.pausePlayer() | ||
321 | } | ||
322 | |||
323 | onVideoRemoved () { | 313 | onVideoRemoved () { |
324 | this.redirectService.redirectToHomepage() | 314 | this.redirectService.redirectToHomepage() |
325 | } | 315 | } |
@@ -396,6 +386,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
396 | this.loadVideo(videoId) | 386 | this.loadVideo(videoId) |
397 | } | 387 | } |
398 | 388 | ||
389 | displayOtherVideosAsRow () { | ||
390 | // Use the same value as in the SASS file | ||
391 | return this.screenService.getWindowInnerWidth() <= 1100 | ||
392 | } | ||
393 | |||
399 | private loadVideo (videoId: string) { | 394 | private loadVideo (videoId: string) { |
400 | // Video did not change | 395 | // Video did not change |
401 | if (this.video && this.video.uuid === videoId) return | 396 | if (this.video && this.video.uuid === videoId) return |
@@ -768,9 +763,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
768 | const getStartTime = () => { | 763 | const getStartTime = () => { |
769 | const byUrl = urlOptions.startTime !== undefined | 764 | const byUrl = urlOptions.startTime !== undefined |
770 | const byHistory = video.userHistory && (!this.playlist || urlOptions.resume !== undefined) | 765 | const byHistory = video.userHistory && (!this.playlist || urlOptions.resume !== undefined) |
766 | const byLocalStorage = getStoredVideoWatchHistory(video.uuid) | ||
771 | 767 | ||
772 | if (byUrl) return timeToInt(urlOptions.startTime) | 768 | if (byUrl) return timeToInt(urlOptions.startTime) |
773 | if (byHistory) return video.userHistory.currentTime | 769 | if (byHistory) return video.userHistory.currentTime |
770 | if (byLocalStorage) return byLocalStorage.duration | ||
774 | 771 | ||
775 | return 0 | 772 | return 0 |
776 | } | 773 | } |
@@ -815,6 +812,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
815 | ? this.videoService.getVideoViewUrl(video.uuid) | 812 | ? this.videoService.getVideoViewUrl(video.uuid) |
816 | : null, | 813 | : null, |
817 | embedUrl: video.embedUrl, | 814 | embedUrl: video.embedUrl, |
815 | embedTitle: video.name, | ||
818 | 816 | ||
819 | isLive: video.isLive, | 817 | isLive: video.isLive, |
820 | 818 | ||
@@ -827,7 +825,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
827 | 825 | ||
828 | serverUrl: environment.apiUrl, | 826 | serverUrl: environment.apiUrl, |
829 | 827 | ||
830 | videoCaptions: playerCaptions | 828 | videoCaptions: playerCaptions, |
829 | |||
830 | videoUUID: video.uuid | ||
831 | }, | 831 | }, |
832 | 832 | ||
833 | webtorrent: { | 833 | webtorrent: { |
@@ -867,24 +867,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
867 | return { playerMode: mode, playerOptions: options } | 867 | return { playerMode: mode, playerOptions: options } |
868 | } | 868 | } |
869 | 869 | ||
870 | private pausePlayer () { | ||
871 | if (!this.player) return | ||
872 | |||
873 | this.player.pause() | ||
874 | } | ||
875 | |||
876 | private resumePlayer () { | ||
877 | if (!this.player) return | ||
878 | |||
879 | this.player.play() | ||
880 | } | ||
881 | |||
882 | private isPlaying () { | ||
883 | if (!this.player) return | ||
884 | |||
885 | return !this.player.paused() | ||
886 | } | ||
887 | |||
888 | private async subscribeToLiveEventsIfNeeded (oldVideo: VideoDetails, newVideo: VideoDetails) { | 870 | private async subscribeToLiveEventsIfNeeded (oldVideo: VideoDetails, newVideo: VideoDetails) { |
889 | if (!this.liveVideosSub) { | 871 | if (!this.liveVideosSub) { |
890 | this.liveVideosSub = this.buildLiveEventsSubscription() | 872 | this.liveVideosSub = this.buildLiveEventsSubscription() |
diff --git a/client/src/app/+videos/+video-watch/video-watch.module.ts b/client/src/app/+videos/+video-watch/video-watch.module.ts index fbda9b9c4..3e9f3822e 100644 --- a/client/src/app/+videos/+video-watch/video-watch.module.ts +++ b/client/src/app/+videos/+video-watch/video-watch.module.ts | |||
@@ -4,6 +4,7 @@ import { SharedGlobalIconModule } from '@app/shared/shared-icons' | |||
4 | import { SharedMainModule } from '@app/shared/shared-main' | 4 | import { SharedMainModule } from '@app/shared/shared-main' |
5 | import { SharedModerationModule } from '@app/shared/shared-moderation' | 5 | import { SharedModerationModule } from '@app/shared/shared-moderation' |
6 | import { SharedShareModal } from '@app/shared/shared-share-modal' | 6 | import { SharedShareModal } from '@app/shared/shared-share-modal' |
7 | import { SharedSupportModal } from '@app/shared/shared-support-modal' | ||
7 | import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription' | 8 | import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription' |
8 | import { SharedVideoModule } from '@app/shared/shared-video' | 9 | import { SharedVideoModule } from '@app/shared/shared-video' |
9 | import { SharedVideoCommentModule } from '@app/shared/shared-video-comment' | 10 | import { SharedVideoCommentModule } from '@app/shared/shared-video-comment' |
@@ -13,9 +14,9 @@ import { VideoCommentService } from '../../shared/shared-video-comment/video-com | |||
13 | import { VideoCommentAddComponent } from './comment/video-comment-add.component' | 14 | import { VideoCommentAddComponent } from './comment/video-comment-add.component' |
14 | import { VideoCommentComponent } from './comment/video-comment.component' | 15 | import { VideoCommentComponent } from './comment/video-comment.component' |
15 | import { VideoCommentsComponent } from './comment/video-comments.component' | 16 | import { VideoCommentsComponent } from './comment/video-comments.component' |
16 | import { VideoSupportComponent } from './modal/video-support.component' | ||
17 | import { RecommendationsModule } from './recommendations/recommendations.module' | 17 | import { RecommendationsModule } from './recommendations/recommendations.module' |
18 | import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive' | 18 | import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive' |
19 | import { VideoAvatarChannelComponent } from './video-avatar-channel.component' | ||
19 | import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' | 20 | import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' |
20 | import { VideoWatchRoutingModule } from './video-watch-routing.module' | 21 | import { VideoWatchRoutingModule } from './video-watch-routing.module' |
21 | import { VideoWatchComponent } from './video-watch.component' | 22 | import { VideoWatchComponent } from './video-watch.component' |
@@ -34,18 +35,20 @@ import { VideoWatchComponent } from './video-watch.component' | |||
34 | SharedGlobalIconModule, | 35 | SharedGlobalIconModule, |
35 | SharedVideoCommentModule, | 36 | SharedVideoCommentModule, |
36 | SharedShareModal, | 37 | SharedShareModal, |
37 | SharedVideoModule | 38 | SharedVideoModule, |
39 | SharedSupportModal | ||
38 | ], | 40 | ], |
39 | 41 | ||
40 | declarations: [ | 42 | declarations: [ |
41 | VideoWatchComponent, | 43 | VideoWatchComponent, |
42 | VideoWatchPlaylistComponent, | 44 | VideoWatchPlaylistComponent, |
43 | 45 | ||
44 | VideoSupportComponent, | ||
45 | VideoCommentsComponent, | 46 | VideoCommentsComponent, |
46 | VideoCommentAddComponent, | 47 | VideoCommentAddComponent, |
47 | VideoCommentComponent, | 48 | VideoCommentComponent, |
48 | 49 | ||
50 | VideoAvatarChannelComponent, | ||
51 | |||
49 | TimestampRouteTransformerDirective, | 52 | TimestampRouteTransformerDirective, |
50 | TimestampRouteTransformerDirective | 53 | TimestampRouteTransformerDirective |
51 | ], | 54 | ], |
diff --git a/client/src/app/+videos/video-list/overview/video-overview.component.html b/client/src/app/+videos/video-list/overview/video-overview.component.html index ca986c634..639a96c43 100644 --- a/client/src/app/+videos/video-list/overview/video-overview.component.html +++ b/client/src/app/+videos/video-list/overview/video-overview.component.html | |||
@@ -14,7 +14,7 @@ | |||
14 | </h1> | 14 | </h1> |
15 | 15 | ||
16 | <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)"> | 16 | <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)"> |
17 | <my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="true"> | 17 | <my-video-miniature [video]="video" [user]="userMiniature" [displayVideoActions]="true"> |
18 | </my-video-miniature> | 18 | </my-video-miniature> |
19 | </div> | 19 | </div> |
20 | </div> | 20 | </div> |
@@ -25,7 +25,7 @@ | |||
25 | </h2> | 25 | </h2> |
26 | 26 | ||
27 | <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)"> | 27 | <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)"> |
28 | <my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="true"> | 28 | <my-video-miniature [video]="video" [user]="userMiniature" [displayVideoActions]="true"> |
29 | </my-video-miniature> | 29 | </my-video-miniature> |
30 | </div> | 30 | </div> |
31 | </div> | 31 | </div> |
@@ -40,7 +40,7 @@ | |||
40 | </div> | 40 | </div> |
41 | 41 | ||
42 | <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)"> | 42 | <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)"> |
43 | <my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="true"> | 43 | <my-video-miniature [video]="video" [user]="userMiniature" [displayVideoActions]="true"> |
44 | </my-video-miniature> | 44 | </my-video-miniature> |
45 | </div> | 45 | </div> |
46 | </div> | 46 | </div> |
diff --git a/client/src/app/+videos/video-list/overview/video-overview.component.scss b/client/src/app/+videos/video-list/overview/video-overview.component.scss index c1d10188a..ec73c628c 100644 --- a/client/src/app/+videos/video-list/overview/video-overview.component.scss +++ b/client/src/app/+videos/video-list/overview/video-overview.component.scss | |||
@@ -8,9 +8,84 @@ | |||
8 | } | 8 | } |
9 | 9 | ||
10 | .margin-content { | 10 | .margin-content { |
11 | @include fluid-videos-miniature-layout; | 11 | @include grid-videos-miniature-layout; |
12 | } | 12 | } |
13 | 13 | ||
14 | .section { | 14 | .section { |
15 | @include miniature-rows; | 15 | &:first-child { |
16 | padding-top: 30px; | ||
17 | |||
18 | .section-title { | ||
19 | border-top: none !important; | ||
20 | } | ||
21 | } | ||
22 | |||
23 | .section-title { | ||
24 | font-size: 24px; | ||
25 | font-weight: $font-semibold; | ||
26 | padding-top: 15px; | ||
27 | margin-bottom: 15px; | ||
28 | display: flex; | ||
29 | justify-content: space-between; | ||
30 | |||
31 | &:not(h2) { | ||
32 | border-top: 1px solid $separator-border-color; | ||
33 | } | ||
34 | |||
35 | a { | ||
36 | &:hover, &:focus:not(.focus-visible), &:active { | ||
37 | text-decoration: none; | ||
38 | outline: none; | ||
39 | } | ||
40 | |||
41 | color: pvar(--mainForegroundColor); | ||
42 | } | ||
43 | } | ||
44 | |||
45 | &.channel { | ||
46 | .section-title { | ||
47 | a { | ||
48 | display: flex; | ||
49 | width: fit-content; | ||
50 | align-items: center; | ||
51 | |||
52 | img { | ||
53 | @include channel-avatar(28px); | ||
54 | |||
55 | margin-right: 8px; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | .followers { | ||
60 | color: pvar(--greyForegroundColor); | ||
61 | font-weight: normal; | ||
62 | font-size: 14px; | ||
63 | margin-left: 10px; | ||
64 | position: relative; | ||
65 | top: 2px; | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | .show-more { | ||
71 | position: relative; | ||
72 | top: -5px; | ||
73 | display: inline-block; | ||
74 | font-size: 16px; | ||
75 | text-transform: uppercase; | ||
76 | color: pvar(--greyForegroundColor); | ||
77 | margin-bottom: 10px; | ||
78 | font-weight: $font-semibold; | ||
79 | text-decoration: none; | ||
80 | } | ||
81 | |||
82 | @media screen and (max-width: $mobile-view) { | ||
83 | max-height: initial; | ||
84 | overflow: initial; | ||
85 | |||
86 | .section-title { | ||
87 | font-size: 17px; | ||
88 | margin-left: 10px; | ||
89 | } | ||
90 | } | ||
16 | } | 91 | } |
diff --git a/client/src/app/+videos/video-list/video-user-subscriptions.component.ts b/client/src/app/+videos/video-list/video-user-subscriptions.component.ts index e352a2b2c..6aabb93a5 100644 --- a/client/src/app/+videos/video-list/video-user-subscriptions.component.ts +++ b/client/src/app/+videos/video-list/video-user-subscriptions.component.ts | |||
@@ -7,7 +7,7 @@ import { HooksService } from '@app/core/plugins/hooks.service' | |||
7 | import { immutableAssign } from '@app/helpers' | 7 | import { immutableAssign } from '@app/helpers' |
8 | import { VideoService } from '@app/shared/shared-main' | 8 | import { VideoService } from '@app/shared/shared-main' |
9 | import { UserSubscriptionService } from '@app/shared/shared-user-subscription' | 9 | import { UserSubscriptionService } from '@app/shared/shared-user-subscription' |
10 | import { AbstractVideoList, OwnerDisplayType } from '@app/shared/shared-video-miniature' | 10 | import { AbstractVideoList } from '@app/shared/shared-video-miniature' |
11 | import { FeedFormat, VideoSortField } from '@shared/models' | 11 | import { FeedFormat, VideoSortField } from '@shared/models' |
12 | import { environment } from '../../../environments/environment' | 12 | import { environment } from '../../../environments/environment' |
13 | import { copyToClipboard } from '../../../root-helpers/utils' | 13 | import { copyToClipboard } from '../../../root-helpers/utils' |
@@ -20,7 +20,6 @@ import { copyToClipboard } from '../../../root-helpers/utils' | |||
20 | export class VideoUserSubscriptionsComponent extends AbstractVideoList implements OnInit, OnDestroy { | 20 | export class VideoUserSubscriptionsComponent extends AbstractVideoList implements OnInit, OnDestroy { |
21 | titlePage: string | 21 | titlePage: string |
22 | sort = '-publishedAt' as VideoSortField | 22 | sort = '-publishedAt' as VideoSortField |
23 | ownerDisplayType: OwnerDisplayType = 'auto' | ||
24 | groupByDate = true | 23 | groupByDate = true |
25 | 24 | ||
26 | constructor ( | 25 | constructor ( |
diff --git a/client/src/app/app.component.scss b/client/src/app/app.component.scss index e8447719a..42293e412 100644 --- a/client/src/app/app.component.scss +++ b/client/src/app/app.component.scss | |||
@@ -43,6 +43,10 @@ $assets-path: '../assets'; | |||
43 | background-color: pvar(--mainForegroundColor); | 43 | background-color: pvar(--mainForegroundColor); |
44 | mask-image: url('#{$assets-path}/images/misc/menu.svg'); | 44 | mask-image: url('#{$assets-path}/images/misc/menu.svg'); |
45 | margin: 0 18px 0 20px; | 45 | margin: 0 18px 0 20px; |
46 | |||
47 | @media screen and (max-width: $mobile-view) { | ||
48 | margin: 0 10px; | ||
49 | } | ||
46 | } | 50 | } |
47 | } | 51 | } |
48 | 52 | ||
@@ -71,16 +75,11 @@ $assets-path: '../assets'; | |||
71 | } | 75 | } |
72 | 76 | ||
73 | @media screen and (max-width: $mobile-view) { | 77 | @media screen and (max-width: $mobile-view) { |
74 | width: 70px; | ||
75 | 78 | ||
76 | .peertube-title { | 79 | .peertube-title { |
77 | display: none; | 80 | display: none; |
78 | } | 81 | } |
79 | } | 82 | } |
80 | |||
81 | @media screen and (max-width: 350px) { | ||
82 | flex: auto; | ||
83 | } | ||
84 | } | 83 | } |
85 | 84 | ||
86 | .header-right { | 85 | .header-right { |
diff --git a/client/src/app/core/notification/peertube-socket.service.ts b/client/src/app/core/notification/peertube-socket.service.ts index bc3f7b893..eab1c63f2 100644 --- a/client/src/app/core/notification/peertube-socket.service.ts +++ b/client/src/app/core/notification/peertube-socket.service.ts | |||
@@ -58,12 +58,11 @@ export class PeerTubeSocket { | |||
58 | this.notificationSocket = this.io(environment.apiUrl + '/user-notifications', { | 58 | this.notificationSocket = this.io(environment.apiUrl + '/user-notifications', { |
59 | query: { accessToken: this.auth.getAccessToken() } | 59 | query: { accessToken: this.auth.getAccessToken() } |
60 | }) | 60 | }) |
61 | |||
62 | this.notificationSocket.on('new-notification', (n: UserNotificationServer) => { | ||
63 | this.ngZone.run(() => this.dispatchNotificationEvent('new', n)) | ||
64 | }) | ||
65 | }) | 61 | }) |
66 | 62 | ||
63 | this.notificationSocket.on('new-notification', (n: UserNotificationServer) => { | ||
64 | this.ngZone.run(() => this.dispatchNotificationEvent('new', n)) | ||
65 | }) | ||
67 | } | 66 | } |
68 | 67 | ||
69 | private async initLiveVideosSocket () { | 68 | private async initLiveVideosSocket () { |
diff --git a/client/src/app/core/plugins/hooks.service.ts b/client/src/app/core/plugins/hooks.service.ts index ec47aa48c..ddde198d2 100644 --- a/client/src/app/core/plugins/hooks.service.ts +++ b/client/src/app/core/plugins/hooks.service.ts | |||
@@ -3,13 +3,29 @@ import { mergeMap, switchMap } from 'rxjs/operators' | |||
3 | import { Injectable } from '@angular/core' | 3 | import { Injectable } from '@angular/core' |
4 | import { PluginService } from '@app/core/plugins/plugin.service' | 4 | import { PluginService } from '@app/core/plugins/plugin.service' |
5 | import { ClientActionHookName, ClientFilterHookName, PluginClientScope } from '@shared/models' | 5 | import { ClientActionHookName, ClientFilterHookName, PluginClientScope } from '@shared/models' |
6 | import { AuthService, AuthStatus } from '../auth' | ||
6 | 7 | ||
7 | type RawFunction<U, T> = (params: U) => T | 8 | type RawFunction<U, T> = (params: U) => T |
8 | type ObservableFunction<U, T> = RawFunction<U, Observable<T>> | 9 | type ObservableFunction<U, T> = RawFunction<U, Observable<T>> |
9 | 10 | ||
10 | @Injectable() | 11 | @Injectable() |
11 | export class HooksService { | 12 | export class HooksService { |
12 | constructor (private pluginService: PluginService) { } | 13 | constructor ( |
14 | private authService: AuthService, | ||
15 | private pluginService: PluginService | ||
16 | ) { | ||
17 | // Run auth hooks | ||
18 | this.authService.userInformationLoaded | ||
19 | .subscribe(() => this.runAction('action:auth-user.information-loaded', 'common', { user: this.authService.getUser() })) | ||
20 | |||
21 | this.authService.loginChangedSource.subscribe(obj => { | ||
22 | if (obj === AuthStatus.LoggedIn) { | ||
23 | this.runAction('action:auth-user.logged-in', 'common') | ||
24 | } else if (obj === AuthStatus.LoggedOut) { | ||
25 | this.runAction('action:auth-user.logged-out', 'common') | ||
26 | } | ||
27 | }) | ||
28 | } | ||
13 | 29 | ||
14 | wrapObsFun | 30 | wrapObsFun |
15 | <P, R, H1 extends ClientFilterHookName, H2 extends ClientFilterHookName> | 31 | <P, R, H1 extends ClientFilterHookName, H2 extends ClientFilterHookName> |
diff --git a/client/src/app/core/plugins/plugin.service.ts b/client/src/app/core/plugins/plugin.service.ts index b755fda2c..54dba5e17 100644 --- a/client/src/app/core/plugins/plugin.service.ts +++ b/client/src/app/core/plugins/plugin.service.ts | |||
@@ -235,6 +235,12 @@ export class PluginService implements ClientHook { | |||
235 | .toPromise() | 235 | .toPromise() |
236 | }, | 236 | }, |
237 | 237 | ||
238 | getServerConfig: () => { | ||
239 | return this.server.getConfig() | ||
240 | .pipe(catchError(res => this.restExtractor.handleError(res))) | ||
241 | .toPromise() | ||
242 | }, | ||
243 | |||
238 | isLoggedIn: () => { | 244 | isLoggedIn: () => { |
239 | return this.authService.isLoggedIn() | 245 | return this.authService.isLoggedIn() |
240 | }, | 246 | }, |
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts index 11288fc54..906191ae1 100644 --- a/client/src/app/core/server/server.service.ts +++ b/client/src/app/core/server/server.service.ts | |||
@@ -98,6 +98,12 @@ export class ServerService { | |||
98 | extensions: [] | 98 | extensions: [] |
99 | } | 99 | } |
100 | }, | 100 | }, |
101 | banner: { | ||
102 | file: { | ||
103 | size: { max: 0 }, | ||
104 | extensions: [] | ||
105 | } | ||
106 | }, | ||
101 | video: { | 107 | video: { |
102 | image: { | 108 | image: { |
103 | size: { max: 0 }, | 109 | size: { max: 0 }, |
diff --git a/client/src/app/core/users/user.model.ts b/client/src/app/core/users/user.model.ts index 15a4f7f82..8aaaa238d 100644 --- a/client/src/app/core/users/user.model.ts +++ b/client/src/app/core/users/user.model.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Account } from '@app/shared/shared-main/account/account.model' | 1 | import { Account } from '@app/shared/shared-main/account/account.model' |
2 | import { hasUserRight } from '@shared/core-utils/users' | 2 | import { hasUserRight } from '@shared/core-utils/users' |
3 | import { | 3 | import { |
4 | Avatar, | 4 | ActorImage, |
5 | NSFWPolicyType, | 5 | NSFWPolicyType, |
6 | User as UserServerModel, | 6 | User as UserServerModel, |
7 | UserAdminFlag, | 7 | UserAdminFlag, |
@@ -131,7 +131,7 @@ export class User implements UserServerModel { | |||
131 | } | 131 | } |
132 | } | 132 | } |
133 | 133 | ||
134 | updateAccountAvatar (newAccountAvatar?: Avatar) { | 134 | updateAccountAvatar (newAccountAvatar?: ActorImage) { |
135 | if (newAccountAvatar) this.account.updateAvatar(newAccountAvatar) | 135 | if (newAccountAvatar) this.account.updateAvatar(newAccountAvatar) |
136 | else this.account.resetAvatar() | 136 | else this.account.resetAvatar() |
137 | } | 137 | } |
diff --git a/client/src/app/core/users/user.service.ts b/client/src/app/core/users/user.service.ts index 33cc1f668..3de83152c 100644 --- a/client/src/app/core/users/user.service.ts +++ b/client/src/app/core/users/user.service.ts | |||
@@ -7,8 +7,7 @@ import { AuthService } from '@app/core/auth' | |||
7 | import { getBytes } from '@root-helpers/bytes' | 7 | import { getBytes } from '@root-helpers/bytes' |
8 | import { UserLocalStorageKeys } from '@root-helpers/users' | 8 | import { UserLocalStorageKeys } from '@root-helpers/users' |
9 | import { | 9 | import { |
10 | Avatar, | 10 | ActorImage, |
11 | NSFWPolicyType, | ||
12 | ResultList, | 11 | ResultList, |
13 | User as UserServerModel, | 12 | User as UserServerModel, |
14 | UserCreate, | 13 | UserCreate, |
@@ -136,7 +135,7 @@ export class UserService { | |||
136 | changeAvatar (avatarForm: FormData) { | 135 | changeAvatar (avatarForm: FormData) { |
137 | const url = UserService.BASE_USERS_URL + 'me/avatar/pick' | 136 | const url = UserService.BASE_USERS_URL + 'me/avatar/pick' |
138 | 137 | ||
139 | return this.authHttp.post<{ avatar: Avatar }>(url, avatarForm) | 138 | return this.authHttp.post<{ avatar: ActorImage }>(url, avatarForm) |
140 | .pipe(catchError(err => this.restExtractor.handleError(err))) | 139 | .pipe(catchError(err => this.restExtractor.handleError(err))) |
141 | } | 140 | } |
142 | 141 | ||
diff --git a/client/src/app/core/wrappers/screen.service.ts b/client/src/app/core/wrappers/screen.service.ts index a085e5bdc..c133b5fe9 100644 --- a/client/src/app/core/wrappers/screen.service.ts +++ b/client/src/app/core/wrappers/screen.service.ts | |||
@@ -38,11 +38,10 @@ export class ScreenService { | |||
38 | 38 | ||
39 | let numberOfVideos = 1 | 39 | let numberOfVideos = 1 |
40 | 40 | ||
41 | if (screenWidth > 1850) numberOfVideos = 7 | 41 | if (screenWidth > 1850) numberOfVideos = 5 |
42 | else if (screenWidth > 1600) numberOfVideos = 6 | 42 | else if (screenWidth > 1600) numberOfVideos = 4 |
43 | else if (screenWidth > 1370) numberOfVideos = 5 | 43 | else if (screenWidth > 1370) numberOfVideos = 3 |
44 | else if (screenWidth > 1100) numberOfVideos = 4 | 44 | else if (screenWidth > 1100) numberOfVideos = 2 |
45 | else if (screenWidth > 850) numberOfVideos = 3 | ||
46 | 45 | ||
47 | return numberOfVideos | 46 | return numberOfVideos |
48 | } | 47 | } |
diff --git a/client/src/app/header/search-typeahead.component.html b/client/src/app/header/search-typeahead.component.html index 03e86b8e6..f84086b4a 100644 --- a/client/src/app/header/search-typeahead.component.html +++ b/client/src/app/header/search-typeahead.component.html | |||
@@ -34,7 +34,8 @@ | |||
34 | 34 | ||
35 | <!-- search instructions, when search input is empty --> | 35 | <!-- search instructions, when search input is empty --> |
36 | <div *ngIf="areInstructionsDisplayed()" id="typeahead-instructions" class="overflow-hidden"> | 36 | <div *ngIf="areInstructionsDisplayed()" id="typeahead-instructions" class="overflow-hidden"> |
37 | <div class="d-flex justify-content-between"> | 37 | <span class="text-muted" i18n>Your query will be matched against video names or descriptions, channel names.</span> |
38 | <div class="d-flex justify-content-between mt-3"> | ||
38 | <label class="small-title" i18n>ADVANCED SEARCH</label> | 39 | <label class="small-title" i18n>ADVANCED SEARCH</label> |
39 | <div class="advanced-search-status c-help"> | 40 | <div class="advanced-search-status c-help"> |
40 | <span [ngClass]="canSearchAnyURI ? 'text-success' : 'text-muted'" i18n-title title="Determines whether you can resolve any distant content, or if this instance only allows doing so for instances it follows."> | 41 | <span [ngClass]="canSearchAnyURI ? 'text-success' : 'text-muted'" i18n-title title="Determines whether you can resolve any distant content, or if this instance only allows doing so for instances it follows."> |
@@ -55,7 +56,6 @@ | |||
55 | <em>UUID</em> <span class="text-muted" i18n>will list the matching video</span> | 56 | <em>UUID</em> <span class="text-muted" i18n>will list the matching video</span> |
56 | </li> | 57 | </li> |
57 | </ul> | 58 | </ul> |
58 | <span class="text-muted" i18n>Any other input will return matching video or channel names.</span> | ||
59 | </div> | 59 | </div> |
60 | </div> | 60 | </div> |
61 | 61 | ||
diff --git a/client/src/app/header/search-typeahead.component.scss b/client/src/app/header/search-typeahead.component.scss index f8d68e986..a60aa38d6 100644 --- a/client/src/app/header/search-typeahead.component.scss +++ b/client/src/app/header/search-typeahead.component.scss | |||
@@ -86,7 +86,7 @@ li.suggestion { | |||
86 | flex: 1; | 86 | flex: 1; |
87 | 87 | ||
88 | input { | 88 | input { |
89 | width: unset; | 89 | width: 70px; |
90 | } | 90 | } |
91 | } | 91 | } |
92 | 92 | ||
diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss index 2ea66e57d..aa247d268 100644 --- a/client/src/app/menu/menu.component.scss +++ b/client/src/app/menu/menu.component.scss | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | $menu-link-icon-size: 22px; | 4 | $menu-link-icon-size: 22px; |
5 | $menu-link-icon-margin-right: 18px; | 5 | $menu-link-icon-margin-right: 18px; |
6 | $footer-links-base-opacity: .8; | ||
6 | 7 | ||
7 | @mixin menu-link { | 8 | @mixin menu-link { |
8 | display: flex; | 9 | display: flex; |
@@ -91,168 +92,168 @@ menu { | |||
91 | align-items: center; | 92 | align-items: center; |
92 | justify-content: left; | 93 | justify-content: left; |
93 | 94 | ||
94 | .logged-in-more { | 95 | my-notification { |
95 | $main-radius: 25px; | 96 | margin-left: auto; |
97 | margin-right: 15px; | ||
98 | } | ||
99 | } | ||
100 | } | ||
96 | 101 | ||
97 | flex: 1; | 102 | .logged-in-more { |
98 | margin-left: 13px; | 103 | $main-radius: 25px; |
99 | border-radius: $main-radius; | ||
100 | transition: all .1s ease-in-out; | ||
101 | cursor: pointer; | ||
102 | 104 | ||
103 | *, & { | 105 | flex: 1; |
104 | line-height: 1; | 106 | margin-left: 13px; |
105 | } | 107 | border-radius: $main-radius; |
108 | transition: all .1s ease-in-out; | ||
109 | cursor: pointer; | ||
106 | 110 | ||
107 | &.show { | 111 | *, & { |
108 | background-color: rgba(255, 255, 255, 0.20); | 112 | line-height: 1; |
109 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .325); | 113 | } |
110 | } | ||
111 | 114 | ||
112 | @mixin display-hints($is-mobile: false) { | 115 | &.show { |
113 | background-color: rgba(255, 255, 255, 0.15); | 116 | background-color: rgba(255, 255, 255, 0.20); |
114 | 117 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .325); | |
115 | @if $is-mobile { | 118 | } |
116 | .dropdown-toggle-indicator { | 119 | |
117 | display: inherit !important; | 120 | @mixin display-hints($is-mobile: false) { |
118 | } | 121 | background-color: rgba(255, 255, 255, 0.15); |
119 | .dropdown-toggle:first-child { | ||
120 | padding-right: 30px !important; | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | 122 | ||
125 | &:hover { | 123 | @if $is-mobile { |
126 | @include display-hints; | 124 | .dropdown-toggle-indicator { |
125 | display: inherit !important; | ||
126 | } | ||
127 | .dropdown-toggle:first-child { | ||
128 | padding-right: 30px !important; | ||
127 | } | 129 | } |
130 | } | ||
131 | } | ||
128 | 132 | ||
129 | /* smartphones and touchscreens */ | 133 | &:hover { |
130 | @media (hover: none) and (pointer: coarse) { | 134 | @include display-hints; |
131 | @include display-hints($is-mobile: true); | 135 | } |
132 | 136 | ||
133 | /* fill space when on mobile */ | 137 | /* smartphones and touchscreens */ |
134 | max-width: calc(100% - 80px); | 138 | @media (hover: none) and (pointer: coarse) { |
135 | .dropdown-toggle { | 139 | @include display-hints($is-mobile: true); |
136 | max-width: 100%; | ||
137 | } | ||
138 | .logged-in-info { | ||
139 | max-width: calc(100% - 45px) !important; | ||
140 | } | ||
141 | 140 | ||
142 | } | 141 | /* fill space when on mobile */ |
142 | max-width: calc(100% - 80px); | ||
143 | .dropdown-toggle { | ||
144 | max-width: 100%; | ||
145 | } | ||
146 | .logged-in-info { | ||
147 | max-width: calc(100% - 45px) !important; | ||
148 | } | ||
143 | 149 | ||
144 | .dropdown-toggle-indicator { | 150 | } |
145 | position: relative; | ||
146 | width: 0; | ||
147 | display: none; | ||
148 | |||
149 | span { | ||
150 | position: absolute; | ||
151 | right: -35px; | ||
152 | top: -8px; | ||
153 | color: grey; | ||
154 | width: $main-radius; | ||
155 | } | ||
156 | } | ||
157 | 151 | ||
158 | .dropdown-toggle { | 152 | .dropdown-toggle-indicator { |
159 | &::after { | 153 | position: relative; |
160 | border: none; | 154 | width: 0; |
161 | } | 155 | display: none; |
162 | } | ||
163 | 156 | ||
164 | .dropdown-toggle:first-child { | 157 | span { |
165 | display: flex; | 158 | position: absolute; |
166 | align-items: center; | 159 | right: -35px; |
167 | padding: 5px 7px; | 160 | top: -8px; |
168 | border-radius: $main-radius; | 161 | color: grey; |
169 | } | 162 | width: $main-radius; |
163 | } | ||
164 | } | ||
170 | 165 | ||
171 | img { | 166 | .dropdown-toggle { |
172 | @include avatar(34px); | 167 | &::after { |
168 | border: none; | ||
169 | } | ||
170 | } | ||
173 | 171 | ||
174 | margin-right: 10px; | 172 | .dropdown-toggle:first-child { |
175 | } | 173 | display: flex; |
174 | align-items: center; | ||
175 | padding: 5px 7px; | ||
176 | border-radius: $main-radius; | ||
177 | } | ||
178 | |||
179 | img { | ||
180 | @include avatar(34px); | ||
176 | 181 | ||
177 | .logged-in-info { | 182 | margin-right: 10px; |
178 | max-width: 105px; | 183 | } |
184 | } | ||
179 | 185 | ||
180 | flex-grow: 1; | 186 | .logged-in-info { |
187 | max-width: 105px; | ||
181 | 188 | ||
182 | .logged-in-display-name, | 189 | flex-grow: 1; |
183 | .logged-in-username { | ||
184 | @include ellipsis; | ||
185 | } | ||
186 | 190 | ||
187 | .logged-in-display-name { | 191 | .logged-in-display-name, |
188 | font-size: 16px; | 192 | .logged-in-username { |
189 | font-weight: $font-semibold; | 193 | @include ellipsis; |
190 | color: pvar(--menuForegroundColor); | 194 | } |
191 | 195 | ||
192 | @include disable-default-a-behaviour; | 196 | .logged-in-display-name { |
193 | } | 197 | font-size: 16px; |
198 | font-weight: $font-semibold; | ||
199 | color: pvar(--menuForegroundColor); | ||
194 | 200 | ||
195 | .logged-in-username { | 201 | @include disable-default-a-behaviour; |
196 | font-size: 13px; | 202 | } |
197 | color: #C6C6C6; | ||
198 | margin-top: 3px; | ||
199 | } | ||
200 | } | ||
201 | } | ||
202 | 203 | ||
203 | my-notification { | 204 | .logged-in-username { |
204 | margin-left: auto; | 205 | font-size: 13px; |
205 | margin-right: 15px; | 206 | color: #C6C6C6; |
206 | } | 207 | margin-top: 3px; |
207 | } | 208 | } |
209 | } | ||
208 | 210 | ||
209 | .logged-in-menu { | 211 | .logged-in-menu { |
210 | display: flex; | 212 | display: flex; |
211 | flex-direction: column; | 213 | flex-direction: column; |
212 | align-items: flex-start; | 214 | align-items: flex-start; |
213 | border-top: 1px solid var(--greyForegroundColor); | 215 | border-top: 1px solid var(--greyForegroundColor); |
214 | line-height: $line-height-normal; | 216 | line-height: $line-height-normal; |
215 | 217 | ||
216 | a { | 218 | a { |
217 | @include menu-link; | 219 | @include menu-link; |
218 | @include disable-default-a-behaviour; | 220 | @include disable-default-a-behaviour; |
219 | 221 | ||
220 | $icon-size: 13px; | 222 | $icon-size: 13px; |
221 | $additional-margin: ($menu-link-icon-size - $icon-size) / 2; | 223 | $additional-margin: ($menu-link-icon-size - $icon-size) / 2; |
222 | 224 | ||
223 | font-size: 14px; | 225 | font-size: 14px; |
224 | width: 100%; | 226 | width: 100%; |
225 | min-height: 35px; | 227 | min-height: 35px; |
226 | 228 | ||
227 | my-global-icon { | 229 | my-global-icon { |
228 | width: $icon-size; | 230 | width: $icon-size; |
229 | height: $icon-size; | 231 | height: $icon-size; |
230 | 232 | ||
231 | // Keep aligned with other icons | 233 | // Keep aligned with other icons |
232 | margin-left: $additional-margin; | 234 | margin-left: $additional-margin; |
233 | 235 | ||
234 | &[iconName="channel"] { | 236 | &[iconName="channel"] { |
235 | margin-top: -2px; | 237 | margin-top: -2px; |
236 | } | ||
237 | } | 238 | } |
239 | } | ||
238 | 240 | ||
239 | &.active, | 241 | &.active, |
240 | &:hover, | 242 | &:hover, |
241 | &:focus-visible { | 243 | &:focus-visible { |
242 | my-global-icon { | 244 | my-global-icon { |
243 | @include apply-svg-color(var(--menuForegroundColor)); | 245 | @include apply-svg-color(var(--menuForegroundColor)); |
244 | } | ||
245 | } | 246 | } |
247 | } | ||
246 | 248 | ||
247 | &.active { | 249 | &.active { |
248 | $border-left-width: 4px; | 250 | $border-left-width: 4px; |
249 | 251 | ||
250 | font-weight: $font-semibold; | 252 | font-weight: $font-semibold; |
251 | border-left: $border-left-width solid var(--mainColor); | 253 | border-left: $border-left-width solid var(--mainColor); |
252 | 254 | ||
253 | my-global-icon { | 255 | my-global-icon { |
254 | margin-left: $additional-margin - $border-left-width; | 256 | margin-left: $additional-margin - $border-left-width; |
255 | } | ||
256 | } | 257 | } |
257 | } | 258 | } |
258 | } | 259 | } |
@@ -333,50 +334,48 @@ menu { | |||
333 | flex-direction: column; | 334 | flex-direction: column; |
334 | padding: 0 $menu-lateral-padding; | 335 | padding: 0 $menu-lateral-padding; |
335 | } | 336 | } |
337 | } | ||
336 | 338 | ||
337 | $footer-links-base-opacity: .8; | 339 | .footer-links { |
338 | 340 | &, > div { | |
339 | .footer-links { | 341 | display: flex; |
340 | &, > div { | 342 | flex-wrap: wrap; |
341 | display: flex; | 343 | } |
342 | flex-wrap: wrap; | ||
343 | } | ||
344 | 344 | ||
345 | a, span[role=button] { | 345 | a, span[role=button] { |
346 | display: inline-block; | 346 | display: inline-block; |
347 | text-decoration: none; | 347 | text-decoration: none; |
348 | color: pvar(--menuForegroundColor); | 348 | color: pvar(--menuForegroundColor); |
349 | opacity: $footer-links-base-opacity; | 349 | opacity: $footer-links-base-opacity; |
350 | white-space: nowrap; | ||
351 | font-size: 90%; | ||
352 | font-weight: 500; | ||
353 | line-height: 1.4rem; | ||
354 | margin-right: 8px; | ||
355 | |||
356 | &.inline-global-icon { | ||
357 | display: inline-flex; | ||
358 | align-items: center; | ||
350 | white-space: nowrap; | 359 | white-space: nowrap; |
351 | font-size: 90%; | 360 | height: 1.4rem; |
352 | font-weight: 500; | 361 | |
353 | line-height: 1.4rem; | 362 | my-global-icon { |
354 | margin-right: 8px; | 363 | @include apply-svg-color(pvar(--menuForegroundColor)); |
355 | 364 | ||
356 | &.inline-global-icon { | 365 | display: flex; |
357 | display: inline-flex; | 366 | width: auto; |
358 | align-items: center; | 367 | height: 90%; |
359 | white-space: nowrap; | 368 | margin-right: .2rem; |
360 | height: 1.4rem; | ||
361 | |||
362 | my-global-icon { | ||
363 | @include apply-svg-color(pvar(--menuForegroundColor)); | ||
364 | |||
365 | display: flex; | ||
366 | width: auto; | ||
367 | height: 90%; | ||
368 | margin-right: .2rem; | ||
369 | } | ||
370 | } | 369 | } |
371 | } | 370 | } |
372 | } | 371 | } |
372 | } | ||
373 | 373 | ||
374 | .footer-copyleft small a { | 374 | .footer-copyleft small a { |
375 | @include disable-default-a-behaviour; | 375 | @include disable-default-a-behaviour; |
376 | 376 | ||
377 | color: pvar(--menuForegroundColor); | 377 | color: pvar(--menuForegroundColor); |
378 | opacity: $footer-links-base-opacity - .2; | 378 | opacity: $footer-links-base-opacity - .2; |
379 | } | ||
380 | } | 379 | } |
381 | 380 | ||
382 | .dropdown { | 381 | .dropdown { |
diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts index ed20d9c01..9b6b7cda5 100644 --- a/client/src/app/menu/menu.component.ts +++ b/client/src/app/menu/menu.component.ts | |||
@@ -10,6 +10,7 @@ import { LanguageChooserComponent } from '@app/menu/language-chooser.component' | |||
10 | import { QuickSettingsModalComponent } from '@app/modal/quick-settings-modal.component' | 10 | import { QuickSettingsModalComponent } from '@app/modal/quick-settings-modal.component' |
11 | import { ServerConfig, UserRight, VideoConstant } from '@shared/models' | 11 | import { ServerConfig, UserRight, VideoConstant } from '@shared/models' |
12 | import { NgbDropdown, NgbDropdownConfig } from '@ng-bootstrap/ng-bootstrap' | 12 | import { NgbDropdown, NgbDropdownConfig } from '@ng-bootstrap/ng-bootstrap' |
13 | import { PeertubeModalService } from '@app/shared/shared-main/peertube-modal/peertube-modal.service' | ||
13 | 14 | ||
14 | const logger = debug('peertube:menu:MenuComponent') | 15 | const logger = debug('peertube:menu:MenuComponent') |
15 | 16 | ||
@@ -54,6 +55,7 @@ export class MenuComponent implements OnInit { | |||
54 | private hotkeysService: HotkeysService, | 55 | private hotkeysService: HotkeysService, |
55 | private screenService: ScreenService, | 56 | private screenService: ScreenService, |
56 | private menuService: MenuService, | 57 | private menuService: MenuService, |
58 | private modalService: PeertubeModalService, | ||
57 | private dropdownConfig: NgbDropdownConfig, | 59 | private dropdownConfig: NgbDropdownConfig, |
58 | private router: Router | 60 | private router: Router |
59 | ) { | 61 | ) { |
@@ -130,6 +132,9 @@ export class MenuComponent implements OnInit { | |||
130 | this.authService.userInformationLoaded | 132 | this.authService.userInformationLoaded |
131 | .subscribe(() => this.buildUserLanguages()) | 133 | .subscribe(() => this.buildUserLanguages()) |
132 | }) | 134 | }) |
135 | |||
136 | this.modalService.openQuickSettingsSubject | ||
137 | .subscribe(() => this.openQuickSettings()) | ||
133 | } | 138 | } |
134 | 139 | ||
135 | isRegistrationAllowed () { | 140 | isRegistrationAllowed () { |
diff --git a/client/src/app/menu/notification.component.scss b/client/src/app/menu/notification.component.scss index 40feb9e66..c65787779 100644 --- a/client/src/app/menu/notification.component.scss +++ b/client/src/app/menu/notification.component.scss | |||
@@ -1,6 +1,9 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | .content { | ||
5 | scrollbar-color: auto; | ||
6 | } | ||
4 | 7 | ||
5 | .notification-inbox-popover { | 8 | .notification-inbox-popover { |
6 | padding: 10px; | 9 | padding: 10px; |
diff --git a/client/src/app/modal/instance-config-warning-modal.component.html b/client/src/app/modal/instance-config-warning-modal.component.html index 5a8adf726..498adfeff 100644 --- a/client/src/app/modal/instance-config-warning-modal.component.html +++ b/client/src/app/modal/instance-config-warning-modal.component.html | |||
@@ -15,7 +15,7 @@ | |||
15 | 15 | ||
16 | <li i18n *ngIf="!about.instance.administrator">Who you are</li> | 16 | <li i18n *ngIf="!about.instance.administrator">Who you are</li> |
17 | <li i18n *ngIf="!about.instance.maintenanceLifetime">How long you plan to maintain your instance</li> | 17 | <li i18n *ngIf="!about.instance.maintenanceLifetime">How long you plan to maintain your instance</li> |
18 | <li i18n *ngIf="!about.instance.businessModel">How you plan to pay your instance</li> | 18 | <li i18n *ngIf="!about.instance.businessModel">How you plan to pay for keeping your instance running</li> |
19 | 19 | ||
20 | <li i18n *ngIf="!about.instance.moderationInformation">How you will moderate your instance</li> | 20 | <li i18n *ngIf="!about.instance.moderationInformation">How you will moderate your instance</li> |
21 | <li i18n *ngIf="!about.instance.terms">Instance terms</li> | 21 | <li i18n *ngIf="!about.instance.terms">Instance terms</li> |
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts index e34836a18..eeb9f128b 100644 --- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts +++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts | |||
@@ -117,7 +117,8 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV | |||
117 | warningTitle: false, | 117 | warningTitle: false, |
118 | startTime: abuse.video.startAt, | 118 | startTime: abuse.video.startAt, |
119 | stopTime: abuse.video.endAt | 119 | stopTime: abuse.video.endAt |
120 | }) | 120 | }), |
121 | abuse.video.name | ||
121 | ) | 122 | ) |
122 | } | 123 | } |
123 | 124 | ||
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html b/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html new file mode 100644 index 000000000..0829263f4 --- /dev/null +++ b/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.html | |||
@@ -0,0 +1,41 @@ | |||
1 | <div class="actor" *ngIf="actor"> | ||
2 | <div class="d-flex"> | ||
3 | <img [ngClass]="{ channel: isChannel() }" [src]="preview || actor.avatarUrl" alt="Avatar" /> | ||
4 | |||
5 | <div class="actor-img-edit-container"> | ||
6 | |||
7 | <div *ngIf="editable && !hasAvatar()" class="actor-img-edit-button" [ngbTooltip]="avatarFormat" placement="right" container="body"> | ||
8 | <my-global-icon iconName="upload"></my-global-icon> | ||
9 | <label class="sr-only" for="avatarfile" i18n>Upload a new avatar</label> | ||
10 | <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/> | ||
11 | </div> | ||
12 | |||
13 | <div | ||
14 | *ngIf="editable && hasAvatar()" class="actor-img-edit-button" | ||
15 | #avatarPopover="ngbPopover" [ngbPopover]="avatarEditContent" popoverClass="popover-image-info" autoClose="outside" placement="right" | ||
16 | > | ||
17 | <my-global-icon iconName="edit"></my-global-icon> | ||
18 | <label class="sr-only" for="avatarMenu" i18n>Change your avatar</label> | ||
19 | </div> | ||
20 | |||
21 | </div> | ||
22 | </div> | ||
23 | |||
24 | <div class="actor-info"> | ||
25 | <div class="actor-info-display-name">{{ actor.displayName }}</div> | ||
26 | <div *ngIf="displayUsername" class="actor-info-username">{{ actor.name }}</div> | ||
27 | <div *ngIf="displaySubscribers" i18n class="actor-info-followers">{{ actor.followersCount }} subscribers</div> | ||
28 | </div> | ||
29 | </div> | ||
30 | |||
31 | <ng-template #avatarEditContent> | ||
32 | <div class="dropdown-item c-hand" [ngbTooltip]="avatarFormat" placement="right" container="body"> | ||
33 | <my-global-icon iconName="upload"></my-global-icon> | ||
34 | <span for="avatarfile" i18n>Upload a new avatar</span> | ||
35 | <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/> | ||
36 | </div> | ||
37 | <div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()"> | ||
38 | <my-global-icon iconName="delete"></my-global-icon> | ||
39 | <span i18n>Remove avatar</span> | ||
40 | </div> | ||
41 | </ng-template> | ||
diff --git a/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss b/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss new file mode 100644 index 000000000..8b0172315 --- /dev/null +++ b/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.scss | |||
@@ -0,0 +1,54 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .actor { | ||
5 | display: flex; | ||
6 | |||
7 | img { | ||
8 | margin-right: 15px; | ||
9 | |||
10 | &:not(.channel) { | ||
11 | @include avatar(100px); | ||
12 | } | ||
13 | |||
14 | &.channel { | ||
15 | @include channel-avatar(100px); | ||
16 | } | ||
17 | } | ||
18 | |||
19 | .actor-info { | ||
20 | display: inline-flex; | ||
21 | flex-direction: column; | ||
22 | |||
23 | .actor-info-display-name { | ||
24 | font-size: 20px; | ||
25 | font-weight: $font-bold; | ||
26 | |||
27 | @media screen and (max-width: $small-view) { | ||
28 | font-size: 16px; | ||
29 | } | ||
30 | } | ||
31 | |||
32 | .actor-info-username { | ||
33 | position: relative; | ||
34 | font-size: 14px; | ||
35 | color: pvar(--greyForegroundColor); | ||
36 | } | ||
37 | |||
38 | .actor-info-followers { | ||
39 | font-size: 15px; | ||
40 | padding-bottom: .5rem; | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | |||
45 | .actor-img-edit-container { | ||
46 | position: relative; | ||
47 | width: 0; | ||
48 | } | ||
49 | |||
50 | .actor-img-edit-button { | ||
51 | top: 55px; | ||
52 | right: 45px; | ||
53 | border-radius: 50%; | ||
54 | } | ||
diff --git a/client/src/app/shared/shared-main/account/actor-avatar-info.component.ts b/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts index b459c591f..d0d269489 100644 --- a/client/src/app/shared/shared-main/account/actor-avatar-info.component.ts +++ b/client/src/app/shared/shared-actor-image/actor-avatar-edit.component.ts | |||
@@ -1,21 +1,27 @@ | |||
1 | import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core' | 1 | import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser' | ||
2 | import { Notifier, ServerService } from '@app/core' | 3 | import { Notifier, ServerService } from '@app/core' |
4 | import { Account, VideoChannel } from '@app/shared/shared-main' | ||
3 | import { NgbPopover } from '@ng-bootstrap/ng-bootstrap' | 5 | import { NgbPopover } from '@ng-bootstrap/ng-bootstrap' |
4 | import { getBytes } from '@root-helpers/bytes' | 6 | import { getBytes } from '@root-helpers/bytes' |
5 | import { Account } from '../account/account.model' | ||
6 | import { VideoChannel } from '../video-channel/video-channel.model' | ||
7 | import { Actor } from './actor.model' | ||
8 | 7 | ||
9 | @Component({ | 8 | @Component({ |
10 | selector: 'my-actor-avatar-info', | 9 | selector: 'my-actor-avatar-edit', |
11 | templateUrl: './actor-avatar-info.component.html', | 10 | templateUrl: './actor-avatar-edit.component.html', |
12 | styleUrls: [ './actor-avatar-info.component.scss' ] | 11 | styleUrls: [ |
12 | './actor-image-edit.scss', | ||
13 | './actor-avatar-edit.component.scss' | ||
14 | ] | ||
13 | }) | 15 | }) |
14 | export class ActorAvatarInfoComponent implements OnInit, OnChanges { | 16 | export class ActorAvatarEditComponent implements OnInit { |
15 | @ViewChild('avatarfileInput') avatarfileInput: ElementRef<HTMLInputElement> | 17 | @ViewChild('avatarfileInput') avatarfileInput: ElementRef<HTMLInputElement> |
16 | @ViewChild('avatarPopover') avatarPopover: NgbPopover | 18 | @ViewChild('avatarPopover') avatarPopover: NgbPopover |
17 | 19 | ||
18 | @Input() actor: VideoChannel | Account | 20 | @Input() actor: VideoChannel | Account |
21 | @Input() editable = true | ||
22 | @Input() displaySubscribers = true | ||
23 | @Input() displayUsername = true | ||
24 | @Input() previewImage = false | ||
19 | 25 | ||
20 | @Output() avatarChange = new EventEmitter<FormData>() | 26 | @Output() avatarChange = new EventEmitter<FormData>() |
21 | @Output() avatarDelete = new EventEmitter<void>() | 27 | @Output() avatarDelete = new EventEmitter<void>() |
@@ -24,9 +30,10 @@ export class ActorAvatarInfoComponent implements OnInit, OnChanges { | |||
24 | maxAvatarSize = 0 | 30 | maxAvatarSize = 0 |
25 | avatarExtensions = '' | 31 | avatarExtensions = '' |
26 | 32 | ||
27 | private avatarUrl: string | 33 | preview: SafeResourceUrl |
28 | 34 | ||
29 | constructor ( | 35 | constructor ( |
36 | private sanitizer: DomSanitizer, | ||
30 | private serverService: ServerService, | 37 | private serverService: ServerService, |
31 | private notifier: Notifier | 38 | private notifier: Notifier |
32 | ) { } | 39 | ) { } |
@@ -42,12 +49,6 @@ export class ActorAvatarInfoComponent implements OnInit, OnChanges { | |||
42 | }) | 49 | }) |
43 | } | 50 | } |
44 | 51 | ||
45 | ngOnChanges (changes: SimpleChanges) { | ||
46 | if (changes['actor']) { | ||
47 | this.avatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.actor) | ||
48 | } | ||
49 | } | ||
50 | |||
51 | onAvatarChange (input: HTMLInputElement) { | 52 | onAvatarChange (input: HTMLInputElement) { |
52 | this.avatarfileInput = new ElementRef(input) | 53 | this.avatarfileInput = new ElementRef(input) |
53 | 54 | ||
@@ -61,13 +62,22 @@ export class ActorAvatarInfoComponent implements OnInit, OnChanges { | |||
61 | formData.append('avatarfile', avatarfile) | 62 | formData.append('avatarfile', avatarfile) |
62 | this.avatarPopover?.close() | 63 | this.avatarPopover?.close() |
63 | this.avatarChange.emit(formData) | 64 | this.avatarChange.emit(formData) |
65 | |||
66 | if (this.previewImage) { | ||
67 | this.preview = this.sanitizer.bypassSecurityTrustResourceUrl(URL.createObjectURL(avatarfile)) | ||
68 | } | ||
64 | } | 69 | } |
65 | 70 | ||
66 | deleteAvatar () { | 71 | deleteAvatar () { |
72 | this.preview = undefined | ||
67 | this.avatarDelete.emit() | 73 | this.avatarDelete.emit() |
68 | } | 74 | } |
69 | 75 | ||
70 | hasAvatar () { | 76 | hasAvatar () { |
71 | return !!this.avatarUrl | 77 | return !!this.preview || !!this.actor.avatar |
78 | } | ||
79 | |||
80 | isChannel () { | ||
81 | return !!(this.actor as VideoChannel).ownerAccount | ||
72 | } | 82 | } |
73 | } | 83 | } |
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.html b/client/src/app/shared/shared-actor-image/actor-banner-edit.component.html new file mode 100644 index 000000000..266fc26c5 --- /dev/null +++ b/client/src/app/shared/shared-actor-image/actor-banner-edit.component.html | |||
@@ -0,0 +1,34 @@ | |||
1 | <div class="actor" *ngIf="actor"> | ||
2 | <div class="actor-img-edit-container"> | ||
3 | <div class="banner-placeholder"> | ||
4 | <img *ngIf="hasBanner()" [src]="preview || actor.bannerUrl" alt="Banner" /> | ||
5 | </div> | ||
6 | |||
7 | <div *ngIf="!hasBanner()" class="actor-img-edit-button" [ngbTooltip]="bannerFormat" placement="right" container="body"> | ||
8 | <my-global-icon iconName="upload"></my-global-icon> | ||
9 | <label for="bannerfile" i18n>Upload a new banner</label> | ||
10 | <input #bannerfileInput type="file" name="bannerfile" id="bannerfile" [accept]="bannerExtensions" (change)="onBannerChange(bannerfileInput)"/> | ||
11 | </div> | ||
12 | |||
13 | <div | ||
14 | *ngIf="hasBanner()" class="actor-img-edit-button" | ||
15 | #bannerPopover="ngbPopover" [ngbPopover]="bannerEditContent" popoverClass="popover-image-info" autoClose="outside" placement="right" | ||
16 | > | ||
17 | <my-global-icon iconName="edit"></my-global-icon> | ||
18 | <label for="bannerMenu" i18n>Change your banner</label> | ||
19 | </div> | ||
20 | </div> | ||
21 | </div> | ||
22 | |||
23 | <ng-template #bannerEditContent> | ||
24 | <div class="dropdown-item c-hand" [ngbTooltip]="bannerFormat" placement="right" container="body"> | ||
25 | <my-global-icon iconName="upload"></my-global-icon> | ||
26 | <span for="bannerfile" i18n>Upload a new banner</span> | ||
27 | <input #bannerfileInput type="file" name="bannerfile" id="bannerfile" [accept]="bannerExtensions" (change)="onBannerChange(bannerfileInput)"/> | ||
28 | </div> | ||
29 | |||
30 | <div class="dropdown-item c-hand" (click)="deleteBanner()" (key.enter)="deleteBanner()"> | ||
31 | <my-global-icon iconName="delete"></my-global-icon> | ||
32 | <span i18n>Remove banner</span> | ||
33 | </div> | ||
34 | </ng-template> | ||
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss b/client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss new file mode 100644 index 000000000..23606f871 --- /dev/null +++ b/client/src/app/shared/shared-actor-image/actor-banner-edit.component.scss | |||
@@ -0,0 +1,27 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .banner-placeholder { | ||
5 | @include block-ratio('> div, > img', $banner-inverted-ratio); | ||
6 | } | ||
7 | |||
8 | .banner-placeholder { | ||
9 | background-color: pvar(--greyBackgroundColor); | ||
10 | } | ||
11 | |||
12 | .actor-img-edit-container { | ||
13 | position: relative; | ||
14 | display: flex; | ||
15 | justify-content: center; | ||
16 | align-items: center; | ||
17 | } | ||
18 | |||
19 | .actor-img-edit-button { | ||
20 | position: absolute; | ||
21 | width: auto; | ||
22 | |||
23 | label { | ||
24 | font-weight: $font-semibold; | ||
25 | margin-bottom: 0; | ||
26 | } | ||
27 | } | ||
diff --git a/client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts b/client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts new file mode 100644 index 000000000..8c12d3c4c --- /dev/null +++ b/client/src/app/shared/shared-actor-image/actor-banner-edit.component.ts | |||
@@ -0,0 +1,76 @@ | |||
1 | import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core' | ||
2 | import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser' | ||
3 | import { Notifier, ServerService } from '@app/core' | ||
4 | import { VideoChannel } from '@app/shared/shared-main' | ||
5 | import { NgbPopover } from '@ng-bootstrap/ng-bootstrap' | ||
6 | import { getBytes } from '@root-helpers/bytes' | ||
7 | |||
8 | @Component({ | ||
9 | selector: 'my-actor-banner-edit', | ||
10 | templateUrl: './actor-banner-edit.component.html', | ||
11 | styleUrls: [ | ||
12 | './actor-image-edit.scss', | ||
13 | './actor-banner-edit.component.scss' | ||
14 | ] | ||
15 | }) | ||
16 | export class ActorBannerEditComponent implements OnInit { | ||
17 | @ViewChild('bannerfileInput') bannerfileInput: ElementRef<HTMLInputElement> | ||
18 | @ViewChild('bannerPopover') bannerPopover: NgbPopover | ||
19 | |||
20 | @Input() actor: VideoChannel | ||
21 | @Input() previewImage = false | ||
22 | |||
23 | @Output() bannerChange = new EventEmitter<FormData>() | ||
24 | @Output() bannerDelete = new EventEmitter<void>() | ||
25 | |||
26 | bannerFormat = '' | ||
27 | maxBannerSize = 0 | ||
28 | bannerExtensions = '' | ||
29 | |||
30 | preview: SafeResourceUrl | ||
31 | |||
32 | constructor ( | ||
33 | private sanitizer: DomSanitizer, | ||
34 | private serverService: ServerService, | ||
35 | private notifier: Notifier | ||
36 | ) { } | ||
37 | |||
38 | ngOnInit (): void { | ||
39 | this.serverService.getConfig() | ||
40 | .subscribe(config => { | ||
41 | this.maxBannerSize = config.banner.file.size.max | ||
42 | this.bannerExtensions = config.banner.file.extensions.join(', ') | ||
43 | |||
44 | // tslint:disable:max-line-length | ||
45 | this.bannerFormat = $localize`ratio 6/1, recommended size: 1600x266, max size: ${getBytes(this.maxBannerSize)}, extensions: ${this.bannerExtensions}` | ||
46 | }) | ||
47 | } | ||
48 | |||
49 | onBannerChange (input: HTMLInputElement) { | ||
50 | this.bannerfileInput = new ElementRef(input) | ||
51 | |||
52 | const bannerfile = this.bannerfileInput.nativeElement.files[ 0 ] | ||
53 | if (bannerfile.size > this.maxBannerSize) { | ||
54 | this.notifier.error('Error', $localize`This image is too large.`) | ||
55 | return | ||
56 | } | ||
57 | |||
58 | const formData = new FormData() | ||
59 | formData.append('bannerfile', bannerfile) | ||
60 | this.bannerPopover?.close() | ||
61 | this.bannerChange.emit(formData) | ||
62 | |||
63 | if (this.previewImage) { | ||
64 | this.preview = this.sanitizer.bypassSecurityTrustResourceUrl(URL.createObjectURL(bannerfile)) | ||
65 | } | ||
66 | } | ||
67 | |||
68 | deleteBanner () { | ||
69 | this.preview = undefined | ||
70 | this.bannerDelete.emit() | ||
71 | } | ||
72 | |||
73 | hasBanner () { | ||
74 | return !!this.preview || !!this.actor.bannerUrl | ||
75 | } | ||
76 | } | ||
diff --git a/client/src/app/shared/shared-actor-image/actor-image-edit.scss b/client/src/app/shared/shared-actor-image/actor-image-edit.scss new file mode 100644 index 000000000..918955a89 --- /dev/null +++ b/client/src/app/shared/shared-actor-image/actor-image-edit.scss | |||
@@ -0,0 +1,35 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .actor ::ng-deep .popover-image-info .popover-body { | ||
5 | padding: 0; | ||
6 | |||
7 | .dropdown-item { | ||
8 | padding: 6px 10px; | ||
9 | border-radius: 4px; | ||
10 | |||
11 | &:first-child { | ||
12 | @include peertube-file; | ||
13 | display: block; | ||
14 | } | ||
15 | } | ||
16 | } | ||
17 | |||
18 | .actor-img-edit-button { | ||
19 | @include peertube-button-file(21px); | ||
20 | @include button-with-icon(19px); | ||
21 | @include orange-button; | ||
22 | |||
23 | margin-top: 10px; | ||
24 | margin-bottom: 5px; | ||
25 | cursor: pointer; | ||
26 | |||
27 | input { | ||
28 | width: 30px; | ||
29 | height: 30px; | ||
30 | } | ||
31 | |||
32 | my-global-icon { | ||
33 | right: 7px; | ||
34 | } | ||
35 | } | ||
diff --git a/client/src/app/shared/shared-actor-image/index.ts b/client/src/app/shared/shared-actor-image/index.ts new file mode 100644 index 000000000..18a9038eb --- /dev/null +++ b/client/src/app/shared/shared-actor-image/index.ts | |||
@@ -0,0 +1 @@ | |||
export * from './shared-actor-image.module' | |||
diff --git a/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts b/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts new file mode 100644 index 000000000..6044f9925 --- /dev/null +++ b/client/src/app/shared/shared-actor-image/shared-actor-image.module.ts | |||
@@ -0,0 +1,29 @@ | |||
1 | |||
2 | import { CommonModule } from '@angular/common' | ||
3 | import { NgModule } from '@angular/core' | ||
4 | import { SharedGlobalIconModule } from '../shared-icons' | ||
5 | import { SharedMainModule } from '../shared-main' | ||
6 | import { ActorAvatarEditComponent } from './actor-avatar-edit.component' | ||
7 | import { ActorBannerEditComponent } from './actor-banner-edit.component' | ||
8 | |||
9 | @NgModule({ | ||
10 | imports: [ | ||
11 | CommonModule, | ||
12 | |||
13 | SharedMainModule, | ||
14 | SharedGlobalIconModule | ||
15 | ], | ||
16 | |||
17 | declarations: [ | ||
18 | ActorAvatarEditComponent, | ||
19 | ActorBannerEditComponent | ||
20 | ], | ||
21 | |||
22 | exports: [ | ||
23 | ActorAvatarEditComponent, | ||
24 | ActorBannerEditComponent | ||
25 | ], | ||
26 | |||
27 | providers: [ ] | ||
28 | }) | ||
29 | export class SharedActorImageModule { } | ||
diff --git a/client/src/app/shared/shared-forms/input-toggle-hidden.component.html b/client/src/app/shared/shared-forms/input-toggle-hidden.component.html index e7441e4c1..9f252f299 100644 --- a/client/src/app/shared/shared-forms/input-toggle-hidden.component.html +++ b/client/src/app/shared/shared-forms/input-toggle-hidden.component.html | |||
@@ -12,9 +12,10 @@ | |||
12 | 12 | ||
13 | <button | 13 | <button |
14 | *ngIf="withCopy" [cdkCopyToClipboard]="input.value" (click)="activateCopiedMessage()" type="button" | 14 | *ngIf="withCopy" [cdkCopyToClipboard]="input.value" (click)="activateCopiedMessage()" type="button" |
15 | class="btn btn-outline-secondary" i18n-title title="Copy" | 15 | class="btn btn-outline-secondary text-uppercase" i18n-title title="Copy" |
16 | > | 16 | > |
17 | <span class="glyphicon glyphicon-copy"></span> | 17 | <span class="glyphicon glyphicon-duplicate"></span> |
18 | Copy | ||
18 | </button> | 19 | </button> |
19 | </div> | 20 | </div> |
20 | </div> | 21 | </div> |
diff --git a/client/src/app/shared/shared-forms/markdown-textarea.component.scss b/client/src/app/shared/shared-forms/markdown-textarea.component.scss index fcddfea03..8203c7d1c 100644 --- a/client/src/app/shared/shared-forms/markdown-textarea.component.scss +++ b/client/src/app/shared/shared-forms/markdown-textarea.component.scss | |||
@@ -131,7 +131,7 @@ $input-border-radius: 3px; | |||
131 | border-right: none; | 131 | border-right: none; |
132 | 132 | ||
133 | :last-child { | 133 | :last-child { |
134 | margin-right: $not-expanded-horizontal-margins; | 134 | margin-right: pvar(--horizontalMarginContent); |
135 | } | 135 | } |
136 | } | 136 | } |
137 | 137 | ||
diff --git a/client/src/app/shared/shared-forms/select/select-options.component.ts b/client/src/app/shared/shared-forms/select/select-options.component.ts index 2890670e5..8482b9dea 100644 --- a/client/src/app/shared/shared-forms/select/select-options.component.ts +++ b/client/src/app/shared/shared-forms/select/select-options.component.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { Component, forwardRef, Input } from '@angular/core' | 1 | import { Component, forwardRef, HostListener, Input } from '@angular/core' |
2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' | 2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' |
3 | import { SelectOptionsItem } from '../../../../types/select-options-item.model' | 3 | import { SelectOptionsItem } from '../../../../types/select-options-item.model' |
4 | 4 | ||
@@ -26,6 +26,13 @@ export class SelectOptionsComponent implements ControlValueAccessor { | |||
26 | 26 | ||
27 | propagateChange = (_: any) => { /* empty */ } | 27 | propagateChange = (_: any) => { /* empty */ } |
28 | 28 | ||
29 | // Allow plugins to update our value | ||
30 | @HostListener('change', [ '$event.target' ]) | ||
31 | handleChange (event: any) { | ||
32 | this.writeValue(event.value) | ||
33 | this.onModelChange() | ||
34 | } | ||
35 | |||
29 | writeValue (id: number | string) { | 36 | writeValue (id: number | string) { |
30 | this.selectedId = id | 37 | this.selectedId = id |
31 | } | 38 | } |
diff --git a/client/src/app/shared/shared-instance/instance-about-accordion.component.scss b/client/src/app/shared/shared-instance/instance-about-accordion.component.scss index 275600d60..2f6b420e3 100644 --- a/client/src/app/shared/shared-instance/instance-about-accordion.component.scss +++ b/client/src/app/shared/shared-instance/instance-about-accordion.component.scss | |||
@@ -31,7 +31,7 @@ ngb-accordion ::ng-deep { | |||
31 | padding: 0; | 31 | padding: 0; |
32 | 32 | ||
33 | & + .collapse.show { | 33 | & + .collapse.show { |
34 | background-color: var(--submenuColor); | 34 | background-color: var(--submenuBackgroundColor); |
35 | } | 35 | } |
36 | } | 36 | } |
37 | } | 37 | } |
diff --git a/client/src/app/shared/shared-instance/instance-features-table.component.html b/client/src/app/shared/shared-instance/instance-features-table.component.html index ce2557147..d505b6739 100644 --- a/client/src/app/shared/shared-instance/instance-features-table.component.html +++ b/client/src/app/shared/shared-instance/instance-features-table.component.html | |||
@@ -11,7 +11,7 @@ | |||
11 | <tr> | 11 | <tr> |
12 | <th i18n class="label" scope="row"> | 12 | <th i18n class="label" scope="row"> |
13 | <div>Default NSFW/sensitive videos policy</div> | 13 | <div>Default NSFW/sensitive videos policy</div> |
14 | <div class="more-info">can be redefined by the users</div> | 14 | <div class="c-hand more-info" (click)="openQuickSettingsHighlight()">can be redefined by the users</div> |
15 | </th> | 15 | </th> |
16 | 16 | ||
17 | <td class="value">{{ buildNSFWLabel() }}</td> | 17 | <td class="value">{{ buildNSFWLabel() }}</td> |
diff --git a/client/src/app/shared/shared-instance/instance-features-table.component.ts b/client/src/app/shared/shared-instance/instance-features-table.component.ts index 0166157f9..c3b3dfdfd 100644 --- a/client/src/app/shared/shared-instance/instance-features-table.component.ts +++ b/client/src/app/shared/shared-instance/instance-features-table.component.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { ServerService } from '@app/core' | 2 | import { ServerService } from '@app/core' |
3 | import { ServerConfig } from '@shared/models' | 3 | import { ServerConfig } from '@shared/models' |
4 | import { PeertubeModalService } from '../shared-main/peertube-modal/peertube-modal.service' | ||
4 | 5 | ||
5 | @Component({ | 6 | @Component({ |
6 | selector: 'my-instance-features-table', | 7 | selector: 'my-instance-features-table', |
@@ -11,7 +12,10 @@ export class InstanceFeaturesTableComponent implements OnInit { | |||
11 | quotaHelpIndication = '' | 12 | quotaHelpIndication = '' |
12 | serverConfig: ServerConfig | 13 | serverConfig: ServerConfig |
13 | 14 | ||
14 | constructor (private serverService: ServerService) { } | 15 | constructor ( |
16 | private serverService: ServerService, | ||
17 | private modalService: PeertubeModalService | ||
18 | ) { } | ||
15 | 19 | ||
16 | get initialUserVideoQuota () { | 20 | get initialUserVideoQuota () { |
17 | return this.serverConfig.user.videoQuota | 21 | return this.serverConfig.user.videoQuota |
@@ -56,6 +60,10 @@ export class InstanceFeaturesTableComponent implements OnInit { | |||
56 | return this.serverService.getServerVersionAndCommit() | 60 | return this.serverService.getServerVersionAndCommit() |
57 | } | 61 | } |
58 | 62 | ||
63 | openQuickSettingsHighlight () { | ||
64 | this.modalService.openQuickSettingsSubject.next() | ||
65 | } | ||
66 | |||
59 | private getApproximateTime (seconds: number) { | 67 | private getApproximateTime (seconds: number) { |
60 | const hours = Math.floor(seconds / 3600) | 68 | const hours = Math.floor(seconds / 3600) |
61 | let pluralSuffix = '' | 69 | let pluralSuffix = '' |
diff --git a/client/src/app/shared/shared-main/account/account.model.ts b/client/src/app/shared/shared-main/account/account.model.ts index b71a893d1..17fddff09 100644 --- a/client/src/app/shared/shared-main/account/account.model.ts +++ b/client/src/app/shared/shared-main/account/account.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { Account as ServerAccount, Avatar } from '@shared/models' | 1 | import { Account as ServerAccount, ActorImage } from '@shared/models' |
2 | import { Actor } from './actor.model' | 2 | import { Actor } from './actor.model' |
3 | 3 | ||
4 | export class Account extends Actor implements ServerAccount { | 4 | export class Account extends Actor implements ServerAccount { |
@@ -38,7 +38,7 @@ export class Account extends Actor implements ServerAccount { | |||
38 | this.mutedServerByInstance = false | 38 | this.mutedServerByInstance = false |
39 | } | 39 | } |
40 | 40 | ||
41 | updateAvatar (newAvatar: Avatar) { | 41 | updateAvatar (newAvatar: ActorImage) { |
42 | this.avatar = newAvatar | 42 | this.avatar = newAvatar |
43 | 43 | ||
44 | this.updateComputedAttributes() | 44 | this.updateComputedAttributes() |
diff --git a/client/src/app/shared/shared-main/account/actor-avatar-info.component.html b/client/src/app/shared/shared-main/account/actor-avatar-info.component.html deleted file mode 100644 index 30584fd00..000000000 --- a/client/src/app/shared/shared-main/account/actor-avatar-info.component.html +++ /dev/null | |||
@@ -1,43 +0,0 @@ | |||
1 | <ng-container *ngIf="actor"> | ||
2 | <div class="actor"> | ||
3 | <div class="d-flex"> | ||
4 | <img [src]="actor.avatarUrl" alt="Avatar" /> | ||
5 | |||
6 | <div class="actor-img-edit-container"> | ||
7 | |||
8 | <div *ngIf="!hasAvatar()" class="actor-img-edit-button" [ngbTooltip]="avatarFormat" placement="right" container="body"> | ||
9 | <my-global-icon iconName="upload"></my-global-icon> | ||
10 | <label class="sr-only" for="avatarfile" i18n>Upload a new avatar</label> | ||
11 | <input #avatarfileInput type="file" title=" " name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/> | ||
12 | </div> | ||
13 | |||
14 | <div *ngIf="hasAvatar()" class="actor-img-edit-button" #avatarPopover="ngbPopover" [ngbPopover]="avatarEditContent" popoverClass="popover-avatar-info" autoClose="outside" placement="right"> | ||
15 | <my-global-icon iconName="edit"></my-global-icon> | ||
16 | <label class="sr-only" for="avatarMenu" i18n>Change your avatar</label> | ||
17 | </div> | ||
18 | |||
19 | </div> | ||
20 | </div> | ||
21 | |||
22 | |||
23 | <div class="actor-info"> | ||
24 | <div class="actor-info-names"> | ||
25 | <div class="actor-info-display-name">{{ actor.displayName }}</div> | ||
26 | <div class="actor-info-username">{{ actor.name }}</div> | ||
27 | </div> | ||
28 | <div i18n class="actor-info-followers">{{ actor.followersCount }} subscribers</div> | ||
29 | </div> | ||
30 | </div> | ||
31 | </ng-container> | ||
32 | |||
33 | <ng-template #avatarEditContent> | ||
34 | <div class="dropdown-item c-hand" [ngbTooltip]="avatarFormat" placement="right" container="body"> | ||
35 | <my-global-icon iconName="upload"></my-global-icon> | ||
36 | <span for="avatarfile" i18n>Upload a new avatar</span> | ||
37 | <input #avatarfileInput type="file" title=" " name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/> | ||
38 | </div> | ||
39 | <div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()"> | ||
40 | <my-global-icon iconName="delete"></my-global-icon> | ||
41 | <span i18n>Remove avatar</span> | ||
42 | </div> | ||
43 | </ng-template> | ||
diff --git a/client/src/app/shared/shared-main/account/actor-avatar-info.component.scss b/client/src/app/shared/shared-main/account/actor-avatar-info.component.scss deleted file mode 100644 index 57c298508..000000000 --- a/client/src/app/shared/shared-main/account/actor-avatar-info.component.scss +++ /dev/null | |||
@@ -1,86 +0,0 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .actor { | ||
5 | display: flex; | ||
6 | |||
7 | img { | ||
8 | @include avatar(100px); | ||
9 | |||
10 | margin-right: 15px; | ||
11 | } | ||
12 | |||
13 | .actor-img-edit-container { | ||
14 | position: relative; | ||
15 | width: 0; | ||
16 | |||
17 | .actor-img-edit-button { | ||
18 | @include peertube-button-file(21px); | ||
19 | @include button-with-icon(19px); | ||
20 | @include orange-button; | ||
21 | |||
22 | margin-top: 10px; | ||
23 | margin-bottom: 5px; | ||
24 | border-radius: 50%; | ||
25 | top: 55px; | ||
26 | right: 45px; | ||
27 | cursor: pointer; | ||
28 | |||
29 | input { | ||
30 | width: 30px; | ||
31 | height: 30px; | ||
32 | } | ||
33 | |||
34 | my-global-icon { | ||
35 | right: 7px; | ||
36 | } | ||
37 | } | ||
38 | } | ||
39 | |||
40 | .actor-info { | ||
41 | justify-content: center; | ||
42 | display: inline-flex; | ||
43 | flex-direction: column; | ||
44 | |||
45 | .actor-info-names { | ||
46 | display: flex; | ||
47 | align-items: center; | ||
48 | |||
49 | .actor-info-display-name { | ||
50 | font-size: 20px; | ||
51 | font-weight: $font-bold; | ||
52 | |||
53 | @media screen and (max-width: $small-view) { | ||
54 | font-size: 16px; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | .actor-info-username { | ||
59 | margin-left: 7px; | ||
60 | position: relative; | ||
61 | top: 2px; | ||
62 | font-size: 14px; | ||
63 | color: $grey-actor-name; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | .actor-info-followers { | ||
68 | font-size: 15px; | ||
69 | padding-bottom: .5rem; | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | .actor-img-edit-container ::ng-deep .popover-avatar-info .popover-body { | ||
75 | padding: 0; | ||
76 | |||
77 | .dropdown-item { | ||
78 | padding: 6px 10px; | ||
79 | border-radius: 4px; | ||
80 | |||
81 | &:first-child { | ||
82 | @include peertube-file; | ||
83 | display: block; | ||
84 | } | ||
85 | } | ||
86 | } | ||
diff --git a/client/src/app/shared/shared-main/account/actor.model.ts b/client/src/app/shared/shared-main/account/actor.model.ts index 8222c9769..1ee0c297e 100644 --- a/client/src/app/shared/shared-main/account/actor.model.ts +++ b/client/src/app/shared/shared-main/account/actor.model.ts | |||
@@ -1,17 +1,20 @@ | |||
1 | import { Actor as ActorServer, Avatar } from '@shared/models' | 1 | import { Actor as ActorServer, ActorImage } from '@shared/models' |
2 | import { getAbsoluteAPIUrl } from '@app/helpers' | 2 | import { getAbsoluteAPIUrl } from '@app/helpers' |
3 | 3 | ||
4 | export abstract class Actor implements ActorServer { | 4 | export abstract class Actor implements ActorServer { |
5 | id: number | 5 | id: number |
6 | url: string | ||
7 | name: string | 6 | name: string |
7 | |||
8 | host: string | 8 | host: string |
9 | url: string | ||
10 | |||
9 | followingCount: number | 11 | followingCount: number |
10 | followersCount: number | 12 | followersCount: number |
13 | |||
11 | createdAt: Date | string | 14 | createdAt: Date | string |
12 | updatedAt: Date | string | 15 | updatedAt: Date | string |
13 | avatar: Avatar | ||
14 | 16 | ||
17 | avatar: ActorImage | ||
15 | avatarUrl: string | 18 | avatarUrl: string |
16 | 19 | ||
17 | isLocal: boolean | 20 | isLocal: boolean |
@@ -24,6 +27,8 @@ export abstract class Actor implements ActorServer { | |||
24 | 27 | ||
25 | return absoluteAPIUrl + actor.avatar.path | 28 | return absoluteAPIUrl + actor.avatar.path |
26 | } | 29 | } |
30 | |||
31 | return '' | ||
27 | } | 32 | } |
28 | 33 | ||
29 | static CREATE_BY_STRING (accountName: string, host: string, forceHostname = false) { | 34 | static CREATE_BY_STRING (accountName: string, host: string, forceHostname = false) { |
@@ -42,11 +47,11 @@ export abstract class Actor implements ActorServer { | |||
42 | return host.trim() === thisHost | 47 | return host.trim() === thisHost |
43 | } | 48 | } |
44 | 49 | ||
45 | protected constructor (hash: ActorServer) { | 50 | protected constructor (hash: Partial<ActorServer>) { |
46 | this.id = hash.id | 51 | this.id = hash.id |
47 | this.url = hash.url | 52 | this.url = hash.url ?? '' |
48 | this.name = hash.name | 53 | this.name = hash.name ?? '' |
49 | this.host = hash.host | 54 | this.host = hash.host ?? '' |
50 | this.followingCount = hash.followingCount | 55 | this.followingCount = hash.followingCount |
51 | this.followersCount = hash.followersCount | 56 | this.followersCount = hash.followersCount |
52 | 57 | ||
diff --git a/client/src/app/shared/shared-main/account/index.ts b/client/src/app/shared/shared-main/account/index.ts index 61c800e56..b80ddb9f5 100644 --- a/client/src/app/shared/shared-main/account/index.ts +++ b/client/src/app/shared/shared-main/account/index.ts | |||
@@ -1,5 +1,3 @@ | |||
1 | export * from './account.model' | 1 | export * from './account.model' |
2 | export * from './account.service' | 2 | export * from './account.service' |
3 | export * from './actor-avatar-info.component' | ||
4 | export * from './actor.model' | 3 | export * from './actor.model' |
5 | export * from './video-avatar-channel.component' | ||
diff --git a/client/src/app/shared/shared-main/angular/autofocus.directive.ts b/client/src/app/shared/shared-main/angular/autofocus.directive.ts new file mode 100644 index 000000000..5f087d79d --- /dev/null +++ b/client/src/app/shared/shared-main/angular/autofocus.directive.ts | |||
@@ -0,0 +1,12 @@ | |||
1 | import { AfterViewInit, Directive, ElementRef } from '@angular/core' | ||
2 | |||
3 | @Directive({ | ||
4 | selector: '[autofocus]' | ||
5 | }) | ||
6 | export class AutofocusDirective implements AfterViewInit { | ||
7 | constructor (private host: ElementRef) { } | ||
8 | |||
9 | ngAfterViewInit () { | ||
10 | this.host.nativeElement.focus() | ||
11 | } | ||
12 | } | ||
diff --git a/client/src/app/shared/shared-main/angular/index.ts b/client/src/app/shared/shared-main/angular/index.ts index 29f8b3650..8ea47bb33 100644 --- a/client/src/app/shared/shared-main/angular/index.ts +++ b/client/src/app/shared/shared-main/angular/index.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | export * from './autofocus.directive' | ||
1 | export * from './bytes.pipe' | 2 | export * from './bytes.pipe' |
2 | export * from './duration-formatter.pipe' | 3 | export * from './duration-formatter.pipe' |
3 | export * from './from-now.pipe' | 4 | export * from './from-now.pipe' |
diff --git a/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts b/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts index 3ddaffbdf..4fe3b964d 100644 --- a/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts +++ b/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts | |||
@@ -27,7 +27,9 @@ export class AuthInterceptor implements HttpInterceptor { | |||
27 | catchError((err: HttpErrorResponse) => { | 27 | catchError((err: HttpErrorResponse) => { |
28 | if (err.status === HttpStatusCode.UNAUTHORIZED_401 && err.error && err.error.code === 'invalid_token') { | 28 | if (err.status === HttpStatusCode.UNAUTHORIZED_401 && err.error && err.error.code === 'invalid_token') { |
29 | return this.handleTokenExpired(req, next) | 29 | return this.handleTokenExpired(req, next) |
30 | } else if (err.status === HttpStatusCode.UNAUTHORIZED_401) { | 30 | } |
31 | |||
32 | if (err.status === HttpStatusCode.UNAUTHORIZED_401) { | ||
31 | return this.handleNotAuthenticated(err) | 33 | return this.handleNotAuthenticated(err) |
32 | } | 34 | } |
33 | 35 | ||
diff --git a/client/src/app/shared/shared-main/misc/simple-search-input.component.html b/client/src/app/shared/shared-main/misc/simple-search-input.component.html index fb0d97122..c20c02e23 100644 --- a/client/src/app/shared/shared-main/misc/simple-search-input.component.html +++ b/client/src/app/shared/shared-main/misc/simple-search-input.component.html | |||
@@ -1,14 +1,15 @@ | |||
1 | <span> | 1 | <div class="root"> |
2 | <my-global-icon iconName="search" aria-label="Search" role="button" (click)="showInput()"></my-global-icon> | ||
3 | |||
4 | <input | 2 | <input |
5 | #ref | 3 | #ref |
6 | type="text" | 4 | type="text" |
7 | [(ngModel)]="value" | 5 | [(ngModel)]="value" |
8 | (focusout)="focusLost()" | ||
9 | (keyup.enter)="searchChange()" | 6 | (keyup.enter)="searchChange()" |
10 | [hidden]="!shown" | 7 | [hidden]="!inputShown" |
11 | [name]="name" | 8 | [name]="name" |
12 | [placeholder]="placeholder" | 9 | [placeholder]="placeholder" |
13 | > | 10 | > |
14 | </span> | 11 | |
12 | <my-global-icon iconName="search" aria-label="Search" role="button" (click)="onIconClick()" [title]="iconTitle"></my-global-icon> | ||
13 | |||
14 | <my-global-icon *ngIf="!alwaysShow && inputShown" i18n-title title="Close search" iconName="cross" (click)="hideInput()"></my-global-icon> | ||
15 | </div> | ||
diff --git a/client/src/app/shared/shared-main/misc/simple-search-input.component.scss b/client/src/app/shared/shared-main/misc/simple-search-input.component.scss index 591b04fb2..5ae48f81b 100644 --- a/client/src/app/shared/shared-main/misc/simple-search-input.component.scss +++ b/client/src/app/shared/shared-main/misc/simple-search-input.component.scss | |||
@@ -1,29 +1,29 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | span { | 4 | .root { |
5 | opacity: .6; | 5 | display: flex; |
6 | |||
7 | &:focus-within { | ||
8 | opacity: 1; | ||
9 | } | ||
10 | } | 6 | } |
11 | 7 | ||
12 | my-global-icon { | 8 | my-global-icon { |
13 | height: 18px; | 9 | height: 28px; |
14 | position: relative; | 10 | width: 28px; |
15 | top: -2px; | 11 | margin-left: 10px; |
16 | } | 12 | cursor: pointer; |
17 | 13 | ||
18 | input { | 14 | &:hover { |
19 | @include peertube-input-text(150px); | 15 | color: pvar(--mainHoverColor); |
16 | } | ||
20 | 17 | ||
21 | height: 22px; // maximum height for the account/video-channels links | 18 | &[iconName=search] { |
22 | padding-left: 10px; | 19 | color: pvar(--mainForegroundColor); |
23 | background-color: transparent; | 20 | } |
24 | border: none; | ||
25 | 21 | ||
26 | &::placeholder { | 22 | &[iconName=cross] { |
27 | font-size: 15px; | 23 | color: pvar(--mainForegroundColor); |
28 | } | 24 | } |
29 | } | 25 | } |
26 | |||
27 | input { | ||
28 | @include peertube-input-text(200px); | ||
29 | } | ||
diff --git a/client/src/app/shared/shared-main/misc/simple-search-input.component.ts b/client/src/app/shared/shared-main/misc/simple-search-input.component.ts index 86ae9ab42..224d71134 100644 --- a/client/src/app/shared/shared-main/misc/simple-search-input.component.ts +++ b/client/src/app/shared/shared-main/misc/simple-search-input.component.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' | ||
2 | import { ActivatedRoute, Router } from '@angular/router' | ||
3 | import { Subject } from 'rxjs' | 1 | import { Subject } from 'rxjs' |
4 | import { debounceTime, distinctUntilChanged } from 'rxjs/operators' | 2 | import { debounceTime, distinctUntilChanged } from 'rxjs/operators' |
3 | import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' | ||
4 | import { ActivatedRoute, Router } from '@angular/router' | ||
5 | 5 | ||
6 | @Component({ | 6 | @Component({ |
7 | selector: 'simple-search-input', | 7 | selector: 'simple-search-input', |
@@ -13,11 +13,14 @@ export class SimpleSearchInputComponent implements OnInit { | |||
13 | 13 | ||
14 | @Input() name = 'search' | 14 | @Input() name = 'search' |
15 | @Input() placeholder = $localize`Search` | 15 | @Input() placeholder = $localize`Search` |
16 | @Input() iconTitle = $localize`Search` | ||
17 | @Input() alwaysShow = true | ||
16 | 18 | ||
17 | @Output() searchChanged = new EventEmitter<string>() | 19 | @Output() searchChanged = new EventEmitter<string>() |
20 | @Output() inputDisplayChanged = new EventEmitter<boolean>() | ||
18 | 21 | ||
19 | value = '' | 22 | value = '' |
20 | shown: boolean | 23 | inputShown: boolean |
21 | 24 | ||
22 | private searchSubject = new Subject<string>() | 25 | private searchSubject = new Subject<string>() |
23 | 26 | ||
@@ -35,20 +38,51 @@ export class SimpleSearchInputComponent implements OnInit { | |||
35 | .subscribe(value => this.searchChanged.emit(value)) | 38 | .subscribe(value => this.searchChanged.emit(value)) |
36 | 39 | ||
37 | this.searchSubject.next(this.value) | 40 | this.searchSubject.next(this.value) |
41 | |||
42 | if (this.isInputShown()) this.showInput(false) | ||
38 | } | 43 | } |
39 | 44 | ||
40 | showInput () { | 45 | isInputShown () { |
41 | this.shown = true | 46 | if (this.alwaysShow) return true |
42 | setTimeout(() => this.input.nativeElement.focus()) | 47 | |
48 | return this.inputShown | ||
49 | } | ||
50 | |||
51 | onIconClick () { | ||
52 | if (!this.isInputShown()) { | ||
53 | this.showInput() | ||
54 | return | ||
55 | } | ||
56 | |||
57 | this.searchChange() | ||
58 | } | ||
59 | |||
60 | showInput (focus = true) { | ||
61 | this.inputShown = true | ||
62 | this.inputDisplayChanged.emit(this.inputShown) | ||
63 | |||
64 | if (focus) { | ||
65 | setTimeout(() => this.input.nativeElement.focus()) | ||
66 | } | ||
67 | } | ||
68 | |||
69 | hideInput () { | ||
70 | this.inputShown = false | ||
71 | |||
72 | if (this.isInputShown() === false) { | ||
73 | this.inputDisplayChanged.emit(this.inputShown) | ||
74 | } | ||
43 | } | 75 | } |
44 | 76 | ||
45 | focusLost () { | 77 | focusLost () { |
46 | if (this.value !== '') return | 78 | if (this.value) return |
47 | this.shown = false | 79 | |
80 | this.hideInput() | ||
48 | } | 81 | } |
49 | 82 | ||
50 | searchChange () { | 83 | searchChange () { |
51 | this.router.navigate(['./search'], { relativeTo: this.route }) | 84 | this.router.navigate([ './search' ], { relativeTo: this.route }) |
85 | |||
52 | this.searchSubject.next(this.value) | 86 | this.searchSubject.next(this.value) |
53 | } | 87 | } |
54 | } | 88 | } |
diff --git a/client/src/app/shared/shared-main/peertube-modal/index.ts b/client/src/app/shared/shared-main/peertube-modal/index.ts new file mode 100644 index 000000000..d631522e4 --- /dev/null +++ b/client/src/app/shared/shared-main/peertube-modal/index.ts | |||
@@ -0,0 +1 @@ | |||
export * from './peertube-modal.service' | |||
diff --git a/client/src/app/shared/shared-main/peertube-modal/peertube-modal.service.ts b/client/src/app/shared/shared-main/peertube-modal/peertube-modal.service.ts new file mode 100644 index 000000000..79da08a5c --- /dev/null +++ b/client/src/app/shared/shared-main/peertube-modal/peertube-modal.service.ts | |||
@@ -0,0 +1,7 @@ | |||
1 | import { Injectable } from '@angular/core' | ||
2 | import { Subject } from 'rxjs' | ||
3 | |||
4 | @Injectable({ providedIn: 'root' }) | ||
5 | export class PeertubeModalService { | ||
6 | openQuickSettingsSubject = new Subject<void>() | ||
7 | } | ||
diff --git a/client/src/app/shared/shared-main/shared-main.module.ts b/client/src/app/shared/shared-main/shared-main.module.ts index 9d550996d..16d230f46 100644 --- a/client/src/app/shared/shared-main/shared-main.module.ts +++ b/client/src/app/shared/shared-main/shared-main.module.ts | |||
@@ -6,19 +6,20 @@ import { NgModule } from '@angular/core' | |||
6 | import { FormsModule, ReactiveFormsModule } from '@angular/forms' | 6 | import { FormsModule, ReactiveFormsModule } from '@angular/forms' |
7 | import { RouterModule } from '@angular/router' | 7 | import { RouterModule } from '@angular/router' |
8 | import { | 8 | import { |
9 | NgbButtonsModule, | ||
9 | NgbCollapseModule, | 10 | NgbCollapseModule, |
10 | NgbDropdownModule, | 11 | NgbDropdownModule, |
11 | NgbModalModule, | 12 | NgbModalModule, |
12 | NgbNavModule, | 13 | NgbNavModule, |
13 | NgbPopoverModule, | 14 | NgbPopoverModule, |
14 | NgbTooltipModule, | 15 | NgbTooltipModule |
15 | NgbButtonsModule | ||
16 | } from '@ng-bootstrap/ng-bootstrap' | 16 | } from '@ng-bootstrap/ng-bootstrap' |
17 | import { LoadingBarModule } from '@ngx-loading-bar/core' | 17 | import { LoadingBarModule } from '@ngx-loading-bar/core' |
18 | import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client' | 18 | import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client' |
19 | import { SharedGlobalIconModule } from '../shared-icons' | 19 | import { SharedGlobalIconModule } from '../shared-icons' |
20 | import { AccountService, ActorAvatarInfoComponent, VideoAvatarChannelComponent } from './account' | 20 | import { AccountService } from './account' |
21 | import { | 21 | import { |
22 | AutofocusDirective, | ||
22 | BytesPipe, | 23 | BytesPipe, |
23 | DurationFormatterPipe, | 24 | DurationFormatterPipe, |
24 | FromNowPipe, | 25 | FromNowPipe, |
@@ -31,7 +32,7 @@ import { ActionDropdownComponent, ButtonComponent, DeleteButtonComponent, EditBu | |||
31 | import { DateToggleComponent } from './date' | 32 | import { DateToggleComponent } from './date' |
32 | import { FeedComponent } from './feeds' | 33 | import { FeedComponent } from './feeds' |
33 | import { LoaderComponent, SmallLoaderComponent } from './loaders' | 34 | import { LoaderComponent, SmallLoaderComponent } from './loaders' |
34 | import { HelpComponent, ListOverflowComponent, TopMenuDropdownComponent, SimpleSearchInputComponent } from './misc' | 35 | import { HelpComponent, ListOverflowComponent, SimpleSearchInputComponent, TopMenuDropdownComponent } from './misc' |
35 | import { UserHistoryService, UserNotificationsComponent, UserNotificationService, UserQuotaComponent } from './users' | 36 | import { UserHistoryService, UserNotificationsComponent, UserNotificationService, UserQuotaComponent } from './users' |
36 | import { RedundancyService, VideoImportService, VideoOwnershipService, VideoService } from './video' | 37 | import { RedundancyService, VideoImportService, VideoOwnershipService, VideoService } from './video' |
37 | import { VideoCaptionService } from './video-caption' | 38 | import { VideoCaptionService } from './video-caption' |
@@ -64,13 +65,11 @@ import { VideoChannelService } from './video-channel' | |||
64 | ], | 65 | ], |
65 | 66 | ||
66 | declarations: [ | 67 | declarations: [ |
67 | VideoAvatarChannelComponent, | ||
68 | ActorAvatarInfoComponent, | ||
69 | |||
70 | FromNowPipe, | 68 | FromNowPipe, |
71 | NumberFormatterPipe, | 69 | NumberFormatterPipe, |
72 | BytesPipe, | 70 | BytesPipe, |
73 | DurationFormatterPipe, | 71 | DurationFormatterPipe, |
72 | AutofocusDirective, | ||
74 | 73 | ||
75 | InfiniteScrollerDirective, | 74 | InfiniteScrollerDirective, |
76 | PeerTubeTemplateDirective, | 75 | PeerTubeTemplateDirective, |
@@ -118,13 +117,11 @@ import { VideoChannelService } from './video-channel' | |||
118 | 117 | ||
119 | PrimeSharedModule, | 118 | PrimeSharedModule, |
120 | 119 | ||
121 | VideoAvatarChannelComponent, | ||
122 | ActorAvatarInfoComponent, | ||
123 | |||
124 | FromNowPipe, | 120 | FromNowPipe, |
125 | BytesPipe, | 121 | BytesPipe, |
126 | NumberFormatterPipe, | 122 | NumberFormatterPipe, |
127 | DurationFormatterPipe, | 123 | DurationFormatterPipe, |
124 | AutofocusDirective, | ||
128 | 125 | ||
129 | InfiniteScrollerDirective, | 126 | InfiniteScrollerDirective, |
130 | PeerTubeTemplateDirective, | 127 | PeerTubeTemplateDirective, |
diff --git a/client/src/app/shared/shared-main/users/user-notification.model.ts b/client/src/app/shared/shared-main/users/user-notification.model.ts index 1211995fd..88a4811da 100644 --- a/client/src/app/shared/shared-main/users/user-notification.model.ts +++ b/client/src/app/shared/shared-main/users/user-notification.model.ts | |||
@@ -6,6 +6,7 @@ import { | |||
6 | AbuseState, | 6 | AbuseState, |
7 | ActorInfo, | 7 | ActorInfo, |
8 | FollowState, | 8 | FollowState, |
9 | PluginType, | ||
9 | UserNotification as UserNotificationServer, | 10 | UserNotification as UserNotificationServer, |
10 | UserNotificationType, | 11 | UserNotificationType, |
11 | UserRight, | 12 | UserRight, |
@@ -74,20 +75,40 @@ export class UserNotification implements UserNotificationServer { | |||
74 | } | 75 | } |
75 | } | 76 | } |
76 | 77 | ||
78 | plugin?: { | ||
79 | name: string | ||
80 | type: PluginType | ||
81 | latestVersion: string | ||
82 | } | ||
83 | |||
84 | peertube?: { | ||
85 | latestVersion: string | ||
86 | } | ||
87 | |||
77 | createdAt: string | 88 | createdAt: string |
78 | updatedAt: string | 89 | updatedAt: string |
79 | 90 | ||
80 | // Additional fields | 91 | // Additional fields |
81 | videoUrl?: string | 92 | videoUrl?: string |
82 | commentUrl?: any[] | 93 | commentUrl?: any[] |
94 | |||
83 | abuseUrl?: string | 95 | abuseUrl?: string |
84 | abuseQueryParams?: { [id: string]: string } = {} | 96 | abuseQueryParams?: { [id: string]: string } = {} |
97 | |||
85 | videoAutoBlacklistUrl?: string | 98 | videoAutoBlacklistUrl?: string |
99 | |||
86 | accountUrl?: string | 100 | accountUrl?: string |
101 | |||
87 | videoImportIdentifier?: string | 102 | videoImportIdentifier?: string |
88 | videoImportUrl?: string | 103 | videoImportUrl?: string |
104 | |||
89 | instanceFollowUrl?: string | 105 | instanceFollowUrl?: string |
90 | 106 | ||
107 | peertubeVersionLink?: string | ||
108 | |||
109 | pluginUrl?: string | ||
110 | pluginQueryParams?: { [id: string]: string } = {} | ||
111 | |||
91 | constructor (hash: UserNotificationServer, user: AuthUser) { | 112 | constructor (hash: UserNotificationServer, user: AuthUser) { |
92 | this.id = hash.id | 113 | this.id = hash.id |
93 | this.type = hash.type | 114 | this.type = hash.type |
@@ -114,6 +135,9 @@ export class UserNotification implements UserNotificationServer { | |||
114 | this.actorFollow = hash.actorFollow | 135 | this.actorFollow = hash.actorFollow |
115 | if (this.actorFollow) this.setAccountAvatarUrl(this.actorFollow.follower) | 136 | if (this.actorFollow) this.setAccountAvatarUrl(this.actorFollow.follower) |
116 | 137 | ||
138 | this.plugin = hash.plugin | ||
139 | this.peertube = hash.peertube | ||
140 | |||
117 | this.createdAt = hash.createdAt | 141 | this.createdAt = hash.createdAt |
118 | this.updatedAt = hash.updatedAt | 142 | this.updatedAt = hash.updatedAt |
119 | 143 | ||
@@ -197,6 +221,15 @@ export class UserNotification implements UserNotificationServer { | |||
197 | case UserNotificationType.AUTO_INSTANCE_FOLLOWING: | 221 | case UserNotificationType.AUTO_INSTANCE_FOLLOWING: |
198 | this.instanceFollowUrl = '/admin/follows/following-list' | 222 | this.instanceFollowUrl = '/admin/follows/following-list' |
199 | break | 223 | break |
224 | |||
225 | case UserNotificationType.NEW_PEERTUBE_VERSION: | ||
226 | this.peertubeVersionLink = 'https://joinpeertube.org/news' | ||
227 | break | ||
228 | |||
229 | case UserNotificationType.NEW_PLUGIN_VERSION: | ||
230 | this.pluginUrl = `/admin/plugins/list-installed` | ||
231 | this.pluginQueryParams.pluginType = this.plugin.type + '' | ||
232 | break | ||
200 | } | 233 | } |
201 | } catch (err) { | 234 | } catch (err) { |
202 | this.type = null | 235 | this.type = null |
diff --git a/client/src/app/shared/shared-main/users/user-notifications.component.html b/client/src/app/shared/shared-main/users/user-notifications.component.html index 265af8d55..325f0eaae 100644 --- a/client/src/app/shared/shared-main/users/user-notifications.component.html +++ b/client/src/app/shared/shared-main/users/user-notifications.component.html | |||
@@ -4,7 +4,7 @@ | |||
4 | <div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }" (click)="markAsRead(notification)"> | 4 | <div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }" (click)="markAsRead(notification)"> |
5 | 5 | ||
6 | <ng-container [ngSwitch]="notification.type"> | 6 | <ng-container [ngSwitch]="notification.type"> |
7 | <ng-container *ngSwitchCase="UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION"> | 7 | <ng-container *ngSwitchCase="1"> <!-- UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION --> |
8 | <ng-container *ngIf="notification.video; then hasVideo; else noVideo"></ng-container> | 8 | <ng-container *ngIf="notification.video; then hasVideo; else noVideo"></ng-container> |
9 | 9 | ||
10 | <ng-template #hasVideo> | 10 | <ng-template #hasVideo> |
@@ -26,7 +26,7 @@ | |||
26 | </ng-template> | 26 | </ng-template> |
27 | </ng-container> | 27 | </ng-container> |
28 | 28 | ||
29 | <ng-container *ngSwitchCase="UserNotificationType.UNBLACKLIST_ON_MY_VIDEO"> | 29 | <ng-container *ngSwitchCase="5"> <!-- UserNotificationType.UNBLACKLIST_ON_MY_VIDEO --> |
30 | <my-global-icon iconName="undo" aria-hidden="true"></my-global-icon> | 30 | <my-global-icon iconName="undo" aria-hidden="true"></my-global-icon> |
31 | 31 | ||
32 | <div class="message" i18n> | 32 | <div class="message" i18n> |
@@ -34,7 +34,7 @@ | |||
34 | </div> | 34 | </div> |
35 | </ng-container> | 35 | </ng-container> |
36 | 36 | ||
37 | <ng-container *ngSwitchCase="UserNotificationType.BLACKLIST_ON_MY_VIDEO"> | 37 | <ng-container *ngSwitchCase="4"> <!-- UserNotificationType.BLACKLIST_ON_MY_VIDEO --> |
38 | <my-global-icon iconName="no" aria-hidden="true"></my-global-icon> | 38 | <my-global-icon iconName="no" aria-hidden="true"></my-global-icon> |
39 | 39 | ||
40 | <div class="message" i18n> | 40 | <div class="message" i18n> |
@@ -42,7 +42,7 @@ | |||
42 | </div> | 42 | </div> |
43 | </ng-container> | 43 | </ng-container> |
44 | 44 | ||
45 | <ng-container *ngSwitchCase="UserNotificationType.NEW_ABUSE_FOR_MODERATORS"> | 45 | <ng-container *ngSwitchCase="3"> <!-- UserNotificationType.NEW_ABUSE_FOR_MODERATORS --> |
46 | <my-global-icon iconName="flag" aria-hidden="true"></my-global-icon> | 46 | <my-global-icon iconName="flag" aria-hidden="true"></my-global-icon> |
47 | 47 | ||
48 | <div class="message" *ngIf="notification.videoUrl" i18n> | 48 | <div class="message" *ngIf="notification.videoUrl" i18n> |
@@ -63,7 +63,7 @@ | |||
63 | </div> | 63 | </div> |
64 | </ng-container> | 64 | </ng-container> |
65 | 65 | ||
66 | <ng-container *ngSwitchCase="UserNotificationType.ABUSE_STATE_CHANGE"> | 66 | <ng-container *ngSwitchCase="15"> <!-- UserNotificationType.ABUSE_STATE_CHANGE --> |
67 | <my-global-icon iconName="flag" aria-hidden="true"></my-global-icon> | 67 | <my-global-icon iconName="flag" aria-hidden="true"></my-global-icon> |
68 | 68 | ||
69 | <div class="message" i18n> | 69 | <div class="message" i18n> |
@@ -73,7 +73,7 @@ | |||
73 | </div> | 73 | </div> |
74 | </ng-container> | 74 | </ng-container> |
75 | 75 | ||
76 | <ng-container *ngSwitchCase="UserNotificationType.ABUSE_NEW_MESSAGE"> | 76 | <ng-container *ngSwitchCase="16"> <!-- UserNotificationType.ABUSE_NEW_MESSAGE --> |
77 | <my-global-icon iconName="flag" aria-hidden="true"></my-global-icon> | 77 | <my-global-icon iconName="flag" aria-hidden="true"></my-global-icon> |
78 | 78 | ||
79 | <div class="message" i18n> | 79 | <div class="message" i18n> |
@@ -81,7 +81,7 @@ | |||
81 | </div> | 81 | </div> |
82 | </ng-container> | 82 | </ng-container> |
83 | 83 | ||
84 | <ng-container *ngSwitchCase="UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS"> | 84 | <ng-container *ngSwitchCase="12"> <!-- UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS --> |
85 | <my-global-icon iconName="no" aria-hidden="true"></my-global-icon> | 85 | <my-global-icon iconName="no" aria-hidden="true"></my-global-icon> |
86 | 86 | ||
87 | <div class="message" i18n> | 87 | <div class="message" i18n> |
@@ -89,7 +89,7 @@ | |||
89 | </div> | 89 | </div> |
90 | </ng-container> | 90 | </ng-container> |
91 | 91 | ||
92 | <ng-container *ngSwitchCase="UserNotificationType.NEW_COMMENT_ON_MY_VIDEO"> | 92 | <ng-container *ngSwitchCase="2"> |
93 | <ng-container *ngIf="notification.comment"> | 93 | <ng-container *ngIf="notification.comment"> |
94 | <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl"> | 94 | <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl"> |
95 | <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.comment.account.avatarUrl" /> | 95 | <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.comment.account.avatarUrl" /> |
@@ -109,7 +109,7 @@ | |||
109 | </ng-container> | 109 | </ng-container> |
110 | </ng-container> | 110 | </ng-container> |
111 | 111 | ||
112 | <ng-container *ngSwitchCase="UserNotificationType.MY_VIDEO_PUBLISHED"> | 112 | <ng-container *ngSwitchCase="6"> <!-- UserNotificationType.MY_VIDEO_PUBLISHED --> |
113 | <my-global-icon iconName="film" aria-hidden="true"></my-global-icon> | 113 | <my-global-icon iconName="film" aria-hidden="true"></my-global-icon> |
114 | 114 | ||
115 | <div class="message" i18n> | 115 | <div class="message" i18n> |
@@ -117,7 +117,7 @@ | |||
117 | </div> | 117 | </div> |
118 | </ng-container> | 118 | </ng-container> |
119 | 119 | ||
120 | <ng-container *ngSwitchCase="UserNotificationType.MY_VIDEO_IMPORT_SUCCESS"> | 120 | <ng-container *ngSwitchCase="7"> <!-- UserNotificationType.MY_VIDEO_IMPORT_SUCCESS --> |
121 | <my-global-icon iconName="cloud-download" aria-hidden="true"></my-global-icon> | 121 | <my-global-icon iconName="cloud-download" aria-hidden="true"></my-global-icon> |
122 | 122 | ||
123 | <div class="message" i18n> | 123 | <div class="message" i18n> |
@@ -125,7 +125,7 @@ | |||
125 | </div> | 125 | </div> |
126 | </ng-container> | 126 | </ng-container> |
127 | 127 | ||
128 | <ng-container *ngSwitchCase="UserNotificationType.MY_VIDEO_IMPORT_ERROR"> | 128 | <ng-container *ngSwitchCase="8"> <!-- UserNotificationType.MY_VIDEO_IMPORT_ERROR --> |
129 | <my-global-icon iconName="cloud-error" aria-hidden="true"></my-global-icon> | 129 | <my-global-icon iconName="cloud-error" aria-hidden="true"></my-global-icon> |
130 | 130 | ||
131 | <div class="message" i18n> | 131 | <div class="message" i18n> |
@@ -133,7 +133,7 @@ | |||
133 | </div> | 133 | </div> |
134 | </ng-container> | 134 | </ng-container> |
135 | 135 | ||
136 | <ng-container *ngSwitchCase="UserNotificationType.NEW_USER_REGISTRATION"> | 136 | <ng-container *ngSwitchCase="9"> <!-- UserNotificationType.NEW_USER_REGISTRATION --> |
137 | <my-global-icon iconName="user-add" aria-hidden="true"></my-global-icon> | 137 | <my-global-icon iconName="user-add" aria-hidden="true"></my-global-icon> |
138 | 138 | ||
139 | <div class="message" i18n> | 139 | <div class="message" i18n> |
@@ -141,7 +141,7 @@ | |||
141 | </div> | 141 | </div> |
142 | </ng-container> | 142 | </ng-container> |
143 | 143 | ||
144 | <ng-container *ngSwitchCase="UserNotificationType.NEW_FOLLOW"> | 144 | <ng-container *ngSwitchCase="10"> <!-- UserNotificationType.NEW_FOLLOW --> |
145 | <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl"> | 145 | <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl"> |
146 | <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.actorFollow.follower.avatarUrl" /> | 146 | <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.actorFollow.follower.avatarUrl" /> |
147 | </a> | 147 | </a> |
@@ -154,7 +154,7 @@ | |||
154 | </div> | 154 | </div> |
155 | </ng-container> | 155 | </ng-container> |
156 | 156 | ||
157 | <ng-container *ngSwitchCase="UserNotificationType.COMMENT_MENTION"> | 157 | <ng-container *ngSwitchCase="11"> |
158 | <ng-container *ngIf="notification.comment"> | 158 | <ng-container *ngIf="notification.comment"> |
159 | <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl"> | 159 | <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl"> |
160 | <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.comment.account.avatarUrl" /> | 160 | <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.comment.account.avatarUrl" /> |
@@ -174,7 +174,7 @@ | |||
174 | </ng-container> | 174 | </ng-container> |
175 | </ng-container> | 175 | </ng-container> |
176 | 176 | ||
177 | <ng-container *ngSwitchCase="UserNotificationType.NEW_INSTANCE_FOLLOWER"> | 177 | <ng-container *ngSwitchCase="13"> <!-- UserNotificationType.NEW_INSTANCE_FOLLOWER --> |
178 | <my-global-icon iconName="users" aria-hidden="true"></my-global-icon> | 178 | <my-global-icon iconName="users" aria-hidden="true"></my-global-icon> |
179 | 179 | ||
180 | <div class="message" i18n> | 180 | <div class="message" i18n> |
@@ -183,7 +183,7 @@ | |||
183 | </div> | 183 | </div> |
184 | </ng-container> | 184 | </ng-container> |
185 | 185 | ||
186 | <ng-container *ngSwitchCase="UserNotificationType.AUTO_INSTANCE_FOLLOWING"> | 186 | <ng-container *ngSwitchCase="14"> <!-- UserNotificationType.AUTO_INSTANCE_FOLLOWING --> |
187 | <my-global-icon iconName="users" aria-hidden="true"></my-global-icon> | 187 | <my-global-icon iconName="users" aria-hidden="true"></my-global-icon> |
188 | 188 | ||
189 | <div class="message" i18n> | 189 | <div class="message" i18n> |
@@ -191,6 +191,22 @@ | |||
191 | </div> | 191 | </div> |
192 | </ng-container> | 192 | </ng-container> |
193 | 193 | ||
194 | <ng-container *ngSwitchCase="17"> <!-- UserNotificationType.NEW_PLUGIN_VERSION --> | ||
195 | <my-global-icon iconName="cog" aria-hidden="true"></my-global-icon> | ||
196 | |||
197 | <div class="message" i18n> | ||
198 | <a (click)="markAsRead(notification)" [routerLink]="notification.pluginUrl" [queryParams]="notification.pluginQueryParams">A new version of the plugin/theme {{ notification.plugin.name }}</a> is available: {{ notification.plugin.latestVersion }} | ||
199 | </div> | ||
200 | </ng-container> | ||
201 | |||
202 | <ng-container *ngSwitchCase="18"> <!-- UserNotificationType.NEW_PEERTUBE_VERSION --> | ||
203 | <my-global-icon iconName="cog" aria-hidden="true"></my-global-icon> | ||
204 | |||
205 | <div class="message" i18n> | ||
206 | <a (click)="markAsRead(notification)" [href]="notification.peertubeVersionLink" target="_blank" rel="noopener noreferer">A new version of PeerTube</a> is available: {{ notification.peertube.latestVersion }} | ||
207 | </div> | ||
208 | </ng-container> | ||
209 | |||
194 | <ng-container *ngSwitchDefault> | 210 | <ng-container *ngSwitchDefault> |
195 | <my-global-icon iconName="alert" aria-hidden="true"></my-global-icon> | 211 | <my-global-icon iconName="alert" aria-hidden="true"></my-global-icon> |
196 | 212 | ||
diff --git a/client/src/app/shared/shared-main/users/user-notifications.component.ts b/client/src/app/shared/shared-main/users/user-notifications.component.ts index 387c49d94..d7c722355 100644 --- a/client/src/app/shared/shared-main/users/user-notifications.component.ts +++ b/client/src/app/shared/shared-main/users/user-notifications.component.ts | |||
@@ -21,9 +21,6 @@ export class UserNotificationsComponent implements OnInit { | |||
21 | notifications: UserNotification[] = [] | 21 | notifications: UserNotification[] = [] |
22 | sortField = 'createdAt' | 22 | sortField = 'createdAt' |
23 | 23 | ||
24 | // So we can access it in the template | ||
25 | UserNotificationType = UserNotificationType | ||
26 | |||
27 | componentPagination: ComponentPagination | 24 | componentPagination: ComponentPagination |
28 | 25 | ||
29 | onDataSubject = new Subject<any[]>() | 26 | onDataSubject = new Subject<any[]>() |
@@ -48,7 +45,7 @@ export class UserNotificationsComponent implements OnInit { | |||
48 | } | 45 | } |
49 | 46 | ||
50 | loadNotifications (reset?: boolean) { | 47 | loadNotifications (reset?: boolean) { |
51 | this.userNotificationService.listMyNotifications({ | 48 | const options = { |
52 | pagination: this.componentPagination, | 49 | pagination: this.componentPagination, |
53 | ignoreLoadingBar: this.ignoreLoadingBar, | 50 | ignoreLoadingBar: this.ignoreLoadingBar, |
54 | sort: { | 51 | sort: { |
@@ -56,7 +53,9 @@ export class UserNotificationsComponent implements OnInit { | |||
56 | // if we order by creation date, we want DESC. all other fields are ASC (like unread). | 53 | // if we order by creation date, we want DESC. all other fields are ASC (like unread). |
57 | order: this.sortField === 'createdAt' ? -1 : 1 | 54 | order: this.sortField === 'createdAt' ? -1 : 1 |
58 | } | 55 | } |
59 | }) | 56 | } |
57 | |||
58 | this.userNotificationService.listMyNotifications(options) | ||
60 | .subscribe( | 59 | .subscribe( |
61 | result => { | 60 | result => { |
62 | this.notifications = reset ? result.data : this.notifications.concat(result.data) | 61 | this.notifications = reset ? result.data : this.notifications.concat(result.data) |
diff --git a/client/src/app/shared/shared-main/video-channel/video-channel.model.ts b/client/src/app/shared/shared-main/video-channel/video-channel.model.ts index c6a63fe6c..1ba3fcc0e 100644 --- a/client/src/app/shared/shared-main/video-channel/video-channel.model.ts +++ b/client/src/app/shared/shared-main/video-channel/video-channel.model.ts | |||
@@ -1,15 +1,22 @@ | |||
1 | import { VideoChannel as ServerVideoChannel, ViewsPerDate, Account, Avatar } from '@shared/models' | 1 | import { getAbsoluteAPIUrl } from '@app/helpers' |
2 | import { Account as ServerAccount, ActorImage, VideoChannel as ServerVideoChannel, ViewsPerDate } from '@shared/models' | ||
3 | import { Account } from '../account/account.model' | ||
2 | import { Actor } from '../account/actor.model' | 4 | import { Actor } from '../account/actor.model' |
3 | 5 | ||
4 | export class VideoChannel extends Actor implements ServerVideoChannel { | 6 | export class VideoChannel extends Actor implements ServerVideoChannel { |
5 | displayName: string | 7 | displayName: string |
6 | description: string | 8 | description: string |
7 | support: string | 9 | support: string |
10 | |||
8 | isLocal: boolean | 11 | isLocal: boolean |
12 | |||
9 | nameWithHost: string | 13 | nameWithHost: string |
10 | nameWithHostForced: string | 14 | nameWithHostForced: string |
11 | 15 | ||
12 | ownerAccount?: Account | 16 | banner: ActorImage |
17 | bannerUrl: string | ||
18 | |||
19 | ownerAccount?: ServerAccount | ||
13 | ownerBy?: string | 20 | ownerBy?: string |
14 | ownerAvatarUrl?: string | 21 | ownerAvatarUrl?: string |
15 | 22 | ||
@@ -21,19 +28,33 @@ export class VideoChannel extends Actor implements ServerVideoChannel { | |||
21 | return Actor.GET_ACTOR_AVATAR_URL(actor) || this.GET_DEFAULT_AVATAR_URL() | 28 | return Actor.GET_ACTOR_AVATAR_URL(actor) || this.GET_DEFAULT_AVATAR_URL() |
22 | } | 29 | } |
23 | 30 | ||
31 | static GET_ACTOR_BANNER_URL (channel: ServerVideoChannel) { | ||
32 | if (channel?.banner?.url) return channel.banner.url | ||
33 | |||
34 | if (channel && channel.banner) { | ||
35 | const absoluteAPIUrl = getAbsoluteAPIUrl() | ||
36 | |||
37 | return absoluteAPIUrl + channel.banner.path | ||
38 | } | ||
39 | |||
40 | return '' | ||
41 | } | ||
42 | |||
24 | static GET_DEFAULT_AVATAR_URL () { | 43 | static GET_DEFAULT_AVATAR_URL () { |
25 | return `${window.location.origin}/client/assets/images/default-avatar-videochannel.png` | 44 | return `${window.location.origin}/client/assets/images/default-avatar-videochannel.png` |
26 | } | 45 | } |
27 | 46 | ||
28 | constructor (hash: ServerVideoChannel) { | 47 | constructor (hash: Partial<ServerVideoChannel>) { |
29 | super(hash) | 48 | super(hash) |
30 | 49 | ||
31 | this.updateComputedAttributes() | ||
32 | |||
33 | this.displayName = hash.displayName | 50 | this.displayName = hash.displayName |
34 | this.description = hash.description | 51 | this.description = hash.description |
35 | this.support = hash.support | 52 | this.support = hash.support |
53 | |||
54 | this.banner = hash.banner | ||
55 | |||
36 | this.isLocal = hash.isLocal | 56 | this.isLocal = hash.isLocal |
57 | |||
37 | this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host) | 58 | this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host) |
38 | this.nameWithHostForced = Actor.CREATE_BY_STRING(this.name, this.host, true) | 59 | this.nameWithHostForced = Actor.CREATE_BY_STRING(this.name, this.host, true) |
39 | 60 | ||
@@ -46,22 +67,34 @@ export class VideoChannel extends Actor implements ServerVideoChannel { | |||
46 | if (hash.ownerAccount) { | 67 | if (hash.ownerAccount) { |
47 | this.ownerAccount = hash.ownerAccount | 68 | this.ownerAccount = hash.ownerAccount |
48 | this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host) | 69 | this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host) |
49 | this.ownerAvatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.ownerAccount) | 70 | this.ownerAvatarUrl = Account.GET_ACTOR_AVATAR_URL(this.ownerAccount) |
50 | } | 71 | } |
72 | |||
73 | this.updateComputedAttributes() | ||
51 | } | 74 | } |
52 | 75 | ||
53 | updateAvatar (newAvatar: Avatar) { | 76 | updateAvatar (newAvatar: ActorImage) { |
54 | this.avatar = newAvatar | 77 | this.avatar = newAvatar |
55 | 78 | ||
56 | this.updateComputedAttributes() | 79 | this.updateComputedAttributes() |
57 | } | 80 | } |
58 | 81 | ||
59 | resetAvatar () { | 82 | resetAvatar () { |
60 | this.avatar = null | 83 | this.updateAvatar(null) |
61 | this.avatarUrl = VideoChannel.GET_DEFAULT_AVATAR_URL() | 84 | } |
85 | |||
86 | updateBanner (newBanner: ActorImage) { | ||
87 | this.banner = newBanner | ||
88 | |||
89 | this.updateComputedAttributes() | ||
90 | } | ||
91 | |||
92 | resetBanner () { | ||
93 | this.updateBanner(null) | ||
62 | } | 94 | } |
63 | 95 | ||
64 | private updateComputedAttributes () { | 96 | updateComputedAttributes () { |
65 | this.avatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(this) | 97 | this.avatarUrl = VideoChannel.GET_ACTOR_AVATAR_URL(this) |
98 | this.bannerUrl = VideoChannel.GET_ACTOR_BANNER_URL(this) | ||
66 | } | 99 | } |
67 | } | 100 | } |
diff --git a/client/src/app/shared/shared-main/video-channel/video-channel.service.ts b/client/src/app/shared/shared-main/video-channel/video-channel.service.ts index eff3fad4d..e65261763 100644 --- a/client/src/app/shared/shared-main/video-channel/video-channel.service.ts +++ b/client/src/app/shared/shared-main/video-channel/video-channel.service.ts | |||
@@ -3,7 +3,7 @@ import { catchError, map, tap } 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, RestService } from '@app/core' | 5 | import { ComponentPaginationLight, RestExtractor, RestService } from '@app/core' |
6 | import { Avatar, ResultList, VideoChannel as VideoChannelServer, VideoChannelCreate, VideoChannelUpdate } from '@shared/models' | 6 | import { ActorImage, ResultList, VideoChannel as VideoChannelServer, VideoChannelCreate, VideoChannelUpdate } from '@shared/models' |
7 | import { environment } from '../../../../environments/environment' | 7 | import { environment } from '../../../../environments/environment' |
8 | import { Account } from '../account' | 8 | import { Account } from '../account' |
9 | import { AccountService } from '../account/account.service' | 9 | import { AccountService } from '../account/account.service' |
@@ -82,15 +82,15 @@ export class VideoChannelService { | |||
82 | ) | 82 | ) |
83 | } | 83 | } |
84 | 84 | ||
85 | changeVideoChannelAvatar (videoChannelName: string, avatarForm: FormData) { | 85 | changeVideoChannelImage (videoChannelName: string, avatarForm: FormData, type: 'avatar' | 'banner') { |
86 | const url = VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelName + '/avatar/pick' | 86 | const url = VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelName + '/' + type + '/pick' |
87 | 87 | ||
88 | return this.authHttp.post<{ avatar: Avatar }>(url, avatarForm) | 88 | return this.authHttp.post<{ avatar?: ActorImage, banner?: ActorImage }>(url, avatarForm) |
89 | .pipe(catchError(err => this.restExtractor.handleError(err))) | 89 | .pipe(catchError(err => this.restExtractor.handleError(err))) |
90 | } | 90 | } |
91 | 91 | ||
92 | deleteVideoChannelAvatar (videoChannelName: string) { | 92 | deleteVideoChannelImage (videoChannelName: string, type: 'avatar' | 'banner') { |
93 | const url = VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelName + '/avatar' | 93 | const url = VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelName + '/' + type |
94 | 94 | ||
95 | return this.authHttp.delete(url) | 95 | return this.authHttp.delete(url) |
96 | .pipe( | 96 | .pipe( |
diff --git a/client/src/app/shared/shared-main/video/video.model.ts b/client/src/app/shared/shared-main/video/video.model.ts index adb6e884f..1c2c4a575 100644 --- a/client/src/app/shared/shared-main/video/video.model.ts +++ b/client/src/app/shared/shared-main/video/video.model.ts | |||
@@ -6,7 +6,7 @@ import { Actor } from '@app/shared/shared-main/account/actor.model' | |||
6 | import { VideoChannel } from '@app/shared/shared-main/video-channel/video-channel.model' | 6 | import { VideoChannel } from '@app/shared/shared-main/video-channel/video-channel.model' |
7 | import { peertubeTranslate } from '@shared/core-utils/i18n' | 7 | import { peertubeTranslate } from '@shared/core-utils/i18n' |
8 | import { | 8 | import { |
9 | Avatar, | 9 | ActorImage, |
10 | ServerConfig, | 10 | ServerConfig, |
11 | UserRight, | 11 | UserRight, |
12 | Video as VideoServerModel, | 12 | Video as VideoServerModel, |
@@ -72,7 +72,7 @@ export class Video implements VideoServerModel { | |||
72 | displayName: string | 72 | displayName: string |
73 | url: string | 73 | url: string |
74 | host: string | 74 | host: string |
75 | avatar?: Avatar | 75 | avatar?: ActorImage |
76 | } | 76 | } |
77 | 77 | ||
78 | channel: { | 78 | channel: { |
@@ -81,7 +81,7 @@ export class Video implements VideoServerModel { | |||
81 | displayName: string | 81 | displayName: string |
82 | url: string | 82 | url: string |
83 | host: string | 83 | host: string |
84 | avatar?: Avatar | 84 | avatar?: ActorImage |
85 | } | 85 | } |
86 | 86 | ||
87 | userHistory?: { | 87 | userHistory?: { |
diff --git a/client/src/app/shared/shared-moderation/moderation.scss b/client/src/app/shared/shared-moderation/moderation.scss index 4a4e05535..cdcc12fe0 100644 --- a/client/src/app/shared/shared-moderation/moderation.scss +++ b/client/src/app/shared/shared-moderation/moderation.scss | |||
@@ -32,7 +32,7 @@ | |||
32 | color: pvar(--inputPlaceholderColor); | 32 | color: pvar(--inputPlaceholderColor); |
33 | } | 33 | } |
34 | 34 | ||
35 | @include large-screen-ratio($selector: 'div, ::ng-deep iframe') { | 35 | @include block-ratio($selector: 'div, ::ng-deep iframe') { |
36 | width: 100% !important; | 36 | width: 100% !important; |
37 | height: 100% !important; | 37 | height: 100% !important; |
38 | left: 0; | 38 | left: 0; |
diff --git a/client/src/app/shared/shared-moderation/report-modals/report.component.scss b/client/src/app/shared/shared-moderation/report-modals/report.component.scss index b2606cbd8..0567330f5 100644 --- a/client/src/app/shared/shared-moderation/report-modals/report.component.scss +++ b/client/src/app/shared/shared-moderation/report-modals/report.component.scss | |||
@@ -21,7 +21,7 @@ textarea { | |||
21 | } | 21 | } |
22 | 22 | ||
23 | .screenratio { | 23 | .screenratio { |
24 | @include large-screen-ratio($selector: 'div, ::ng-deep iframe') { | 24 | @include block-ratio($selector: 'div, ::ng-deep iframe') { |
25 | left: 0; | 25 | left: 0; |
26 | }; | 26 | }; |
27 | } | 27 | } |
diff --git a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts index 5b06c0bc7..4ca6f52ad 100644 --- a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts +++ b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts | |||
@@ -61,7 +61,8 @@ export class VideoReportComponent extends FormReactive implements OnInit { | |||
61 | baseUrl: this.video.embedUrl, | 61 | baseUrl: this.video.embedUrl, |
62 | title: false, | 62 | title: false, |
63 | warningTitle: false | 63 | warningTitle: false |
64 | }) | 64 | }), |
65 | this.video.name | ||
65 | ) | 66 | ) |
66 | ) | 67 | ) |
67 | } | 68 | } |
diff --git a/client/src/app/shared/shared-share-modal/video-share.component.ts b/client/src/app/shared/shared-share-modal/video-share.component.ts index b06ff3751..e8760bfcc 100644 --- a/client/src/app/shared/shared-share-modal/video-share.component.ts +++ b/client/src/app/shared/shared-share-modal/video-share.component.ts | |||
@@ -86,14 +86,14 @@ export class VideoShareComponent { | |||
86 | const options = this.getVideoOptions(this.video.embedUrl) | 86 | const options = this.getVideoOptions(this.video.embedUrl) |
87 | 87 | ||
88 | const embedUrl = buildVideoLink(options) | 88 | const embedUrl = buildVideoLink(options) |
89 | return buildVideoOrPlaylistEmbed(embedUrl) | 89 | return buildVideoOrPlaylistEmbed(embedUrl, this.video.name) |
90 | } | 90 | } |
91 | 91 | ||
92 | getPlaylistIframeCode () { | 92 | getPlaylistIframeCode () { |
93 | const options = this.getPlaylistOptions(this.playlist.embedUrl) | 93 | const options = this.getPlaylistOptions(this.playlist.embedUrl) |
94 | 94 | ||
95 | const embedUrl = buildPlaylistLink(options) | 95 | const embedUrl = buildPlaylistLink(options) |
96 | return buildVideoOrPlaylistEmbed(embedUrl) | 96 | return buildVideoOrPlaylistEmbed(embedUrl, this.playlist.displayName) |
97 | } | 97 | } |
98 | 98 | ||
99 | getVideoUrl () { | 99 | getVideoUrl () { |
diff --git a/client/src/app/shared/shared-support-modal/index.ts b/client/src/app/shared/shared-support-modal/index.ts new file mode 100644 index 000000000..f41bb4bc2 --- /dev/null +++ b/client/src/app/shared/shared-support-modal/index.ts | |||
@@ -0,0 +1,3 @@ | |||
1 | export * from './support-modal.component' | ||
2 | |||
3 | export * from './shared-support-modal.module' | ||
diff --git a/client/src/app/shared/shared-support-modal/shared-support-modal.module.ts b/client/src/app/shared/shared-support-modal/shared-support-modal.module.ts new file mode 100644 index 000000000..1101d5535 --- /dev/null +++ b/client/src/app/shared/shared-support-modal/shared-support-modal.module.ts | |||
@@ -0,0 +1,24 @@ | |||
1 | import { NgModule } from '@angular/core' | ||
2 | import { SharedFormModule } from '../shared-forms' | ||
3 | import { SharedGlobalIconModule } from '../shared-icons' | ||
4 | import { SharedMainModule } from '../shared-main/shared-main.module' | ||
5 | import { SupportModalComponent } from './support-modal.component' | ||
6 | |||
7 | @NgModule({ | ||
8 | imports: [ | ||
9 | SharedMainModule, | ||
10 | SharedFormModule, | ||
11 | SharedGlobalIconModule | ||
12 | ], | ||
13 | |||
14 | declarations: [ | ||
15 | SupportModalComponent | ||
16 | ], | ||
17 | |||
18 | exports: [ | ||
19 | SupportModalComponent | ||
20 | ], | ||
21 | |||
22 | providers: [ ] | ||
23 | }) | ||
24 | export class SharedSupportModal { } | ||
diff --git a/client/src/app/+videos/+video-watch/modal/video-support.component.html b/client/src/app/shared/shared-support-modal/support-modal.component.html index 935656d23..4a967987f 100644 --- a/client/src/app/+videos/+video-watch/modal/video-support.component.html +++ b/client/src/app/shared/shared-support-modal/support-modal.component.html | |||
@@ -1,10 +1,10 @@ | |||
1 | <ng-template #modal let-hide="close"> | 1 | <ng-template #modal let-hide="close"> |
2 | <div class="modal-header"> | 2 | <div class="modal-header"> |
3 | <h4 i18n class="modal-title">Support {{ video.account.displayName }}</h4> | 3 | <h4 i18n class="modal-title">Support {{ displayName }}</h4> |
4 | <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon> | 4 | <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon> |
5 | </div> | 5 | </div> |
6 | 6 | ||
7 | <div class="modal-body" [innerHTML]="videoHTMLSupport"></div> | 7 | <div class="modal-body" [innerHTML]="htmlSupport"></div> |
8 | 8 | ||
9 | <div class="modal-footer inputs"> | 9 | <div class="modal-footer inputs"> |
10 | <input | 10 | <input |
diff --git a/client/src/app/+videos/+video-watch/modal/video-support.component.scss b/client/src/app/shared/shared-support-modal/support-modal.component.scss index 184e09027..184e09027 100644 --- a/client/src/app/+videos/+video-watch/modal/video-support.component.scss +++ b/client/src/app/shared/shared-support-modal/support-modal.component.scss | |||
diff --git a/client/src/app/shared/shared-support-modal/support-modal.component.ts b/client/src/app/shared/shared-support-modal/support-modal.component.ts new file mode 100644 index 000000000..ae603c7a8 --- /dev/null +++ b/client/src/app/shared/shared-support-modal/support-modal.component.ts | |||
@@ -0,0 +1,40 @@ | |||
1 | import { Component, Input, ViewChild } from '@angular/core' | ||
2 | import { MarkdownService } from '@app/core' | ||
3 | import { VideoDetails } from '@app/shared/shared-main' | ||
4 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | ||
5 | import { VideoChannel } from '@shared/models' | ||
6 | |||
7 | @Component({ | ||
8 | selector: 'my-support-modal', | ||
9 | templateUrl: './support-modal.component.html', | ||
10 | styleUrls: [ './support-modal.component.scss' ] | ||
11 | }) | ||
12 | export class SupportModalComponent { | ||
13 | @Input() video: VideoDetails = null | ||
14 | @Input() videoChannel: VideoChannel = null | ||
15 | |||
16 | @ViewChild('modal', { static: true }) modal: NgbModal | ||
17 | |||
18 | htmlSupport = '' | ||
19 | displayName = '' | ||
20 | |||
21 | constructor ( | ||
22 | private markdownService: MarkdownService, | ||
23 | private modalService: NgbModal | ||
24 | ) { } | ||
25 | |||
26 | show () { | ||
27 | const modalRef = this.modalService.open(this.modal, { centered: true }) | ||
28 | |||
29 | const support = this.video?.support || this.videoChannel.support | ||
30 | |||
31 | this.markdownService.enhancedMarkdownToHTML(support) | ||
32 | .then(r => this.htmlSupport = r) | ||
33 | |||
34 | this.displayName = this.video | ||
35 | ? this.video.channel.displayName | ||
36 | : this.videoChannel.displayName | ||
37 | |||
38 | return modalRef | ||
39 | } | ||
40 | } | ||
diff --git a/client/src/app/shared/shared-video-miniature/abstract-video-list.html b/client/src/app/shared/shared-video-miniature/abstract-video-list.html index 07f79cd6d..ee5df28be 100644 --- a/client/src/app/shared/shared-video-miniature/abstract-video-list.html +++ b/client/src/app/shared/shared-video-miniature/abstract-video-list.html | |||
@@ -43,7 +43,7 @@ | |||
43 | <div class="no-results" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">No results.</div> | 43 | <div class="no-results" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">No results.</div> |
44 | <div | 44 | <div |
45 | myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()" | 45 | myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()" |
46 | class="videos" | 46 | class="videos" [ngClass]="{ 'display-as-row': displayAsRow() }" |
47 | > | 47 | > |
48 | <ng-container *ngFor="let video of videos; trackBy: videoById;"> | 48 | <ng-container *ngFor="let video of videos; trackBy: videoById;"> |
49 | <h2 class="date-title" *ngIf="getCurrentGroupedDateLabel(video)"> | 49 | <h2 class="date-title" *ngIf="getCurrentGroupedDateLabel(video)"> |
@@ -52,8 +52,7 @@ | |||
52 | 52 | ||
53 | <div class="video-wrapper"> | 53 | <div class="video-wrapper"> |
54 | <my-video-miniature | 54 | <my-video-miniature |
55 | [fitWidth]="true" | 55 | [video]="video" [user]="userMiniature" [displayAsRow]="displayAsRow()" |
56 | [video]="video" [user]="userMiniature" [ownerDisplayType]="ownerDisplayType" | ||
57 | [displayVideoActions]="displayVideoActions" [displayOptions]="displayOptions" | 56 | [displayVideoActions]="displayVideoActions" [displayOptions]="displayOptions" |
58 | (videoBlocked)="removeVideoFromArray(video)" (videoRemoved)="removeVideoFromArray(video)" | 57 | (videoBlocked)="removeVideoFromArray(video)" (videoRemoved)="removeVideoFromArray(video)" |
59 | > | 58 | > |
diff --git a/client/src/app/shared/shared-video-miniature/abstract-video-list.scss b/client/src/app/shared/shared-video-miniature/abstract-video-list.scss index 0a8aa8fa4..6570b63d0 100644 --- a/client/src/app/shared/shared-video-miniature/abstract-video-list.scss +++ b/client/src/app/shared/shared-video-miniature/abstract-video-list.scss | |||
@@ -69,7 +69,16 @@ $iconSize: 16px; | |||
69 | } | 69 | } |
70 | 70 | ||
71 | .margin-content { | 71 | .margin-content { |
72 | @include fluid-videos-miniature-layout; | 72 | @include grid-videos-miniature-layout; |
73 | } | ||
74 | |||
75 | .display-as-row.videos { | ||
76 | margin-left: pvar(--horizontalMarginContent); | ||
77 | margin-right: pvar(--horizontalMarginContent); | ||
78 | |||
79 | .video-wrapper { | ||
80 | margin-bottom: 15px; | ||
81 | } | ||
73 | } | 82 | } |
74 | 83 | ||
75 | @media screen and (max-width: $mobile-view) { | 84 | @media screen and (max-width: $mobile-view) { |
diff --git a/client/src/app/shared/shared-video-miniature/abstract-video-list.ts b/client/src/app/shared/shared-video-miniature/abstract-video-list.ts index c13cb3748..f83380513 100644 --- a/client/src/app/shared/shared-video-miniature/abstract-video-list.ts +++ b/client/src/app/shared/shared-video-miniature/abstract-video-list.ts | |||
@@ -28,8 +28,8 @@ import { isLastMonth, isLastWeek, isThisMonth, isToday, isYesterday } from '@sha | |||
28 | import { ServerConfig, UserRight, VideoFilter, VideoSortField } from '@shared/models' | 28 | import { ServerConfig, UserRight, VideoFilter, VideoSortField } from '@shared/models' |
29 | import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type' | 29 | import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type' |
30 | import { Syndication, Video } from '../shared-main' | 30 | import { Syndication, Video } from '../shared-main' |
31 | import { MiniatureDisplayOptions, OwnerDisplayType } from './video-miniature.component' | ||
32 | import { GenericHeaderComponent, VideoListHeaderComponent } from './video-list-header.component' | 31 | import { GenericHeaderComponent, VideoListHeaderComponent } from './video-list-header.component' |
32 | import { MiniatureDisplayOptions } from './video-miniature.component' | ||
33 | 33 | ||
34 | enum GroupDate { | 34 | enum GroupDate { |
35 | UNKNOWN = 0, | 35 | UNKNOWN = 0, |
@@ -65,7 +65,6 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, AfterConte | |||
65 | loadOnInit = true | 65 | loadOnInit = true |
66 | loadUserVideoPreferences = false | 66 | loadUserVideoPreferences = false |
67 | 67 | ||
68 | ownerDisplayType: OwnerDisplayType = 'account' | ||
69 | displayModerationBlock = false | 68 | displayModerationBlock = false |
70 | titleTooltip: string | 69 | titleTooltip: string |
71 | displayVideoActions = true | 70 | displayVideoActions = true |
@@ -320,6 +319,11 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, AfterConte | |||
320 | viewContainerRef.createComponent(componentFactory, 0, injector) | 319 | viewContainerRef.createComponent(componentFactory, 0, injector) |
321 | } | 320 | } |
322 | 321 | ||
322 | // Can be redefined by child | ||
323 | displayAsRow () { | ||
324 | return false | ||
325 | } | ||
326 | |||
323 | // On videos hook for children that want to do something | 327 | // On videos hook for children that want to do something |
324 | protected onMoreVideos () { /* empty */ } | 328 | protected onMoreVideos () { /* empty */ } |
325 | 329 | ||
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.html b/client/src/app/shared/shared-video-miniature/video-download.component.html index 4608e93e7..8a9218343 100644 --- a/client/src/app/shared/shared-video-miniature/video-download.component.html +++ b/client/src/app/shared/shared-video-miniature/video-download.component.html | |||
@@ -17,85 +17,116 @@ | |||
17 | </div> | 17 | </div> |
18 | 18 | ||
19 | <div class="modal-body"> | 19 | <div class="modal-body"> |
20 | <div class="form-group"> | 20 | <div class="alert alert-warning" *ngIf="isConfidentialVideo()" i18n> |
21 | <div class="alert alert-warning" *ngIf="isConfidentialVideo()" i18n> | 21 | The following link contains a private token and should not be shared with anyone. |
22 | The following link contains a private token and should not be shared with anyone. | 22 | </div> |
23 | </div> | ||
24 | 23 | ||
24 | <ng-container *ngIf="type === 'subtitles'"> | ||
25 | <div class="input-group input-group-sm"> | 25 | <div class="input-group input-group-sm"> |
26 | <div class="input-group-prepend peertube-select-container"> | ||
27 | <select *ngIf="type === 'video'" [(ngModel)]="resolutionId" (ngModelChange)="onResolutionIdChange()"> | ||
28 | <option *ngFor="let file of getVideoFiles()" [value]="file.resolution.id">{{ file.resolution.label }}</option> | ||
29 | </select> | ||
30 | |||
31 | <select *ngIf="type === 'subtitles'" [(ngModel)]="subtitleLanguageId"> | ||
32 | <option *ngFor="let caption of videoCaptions" [value]="caption.language.id">{{ caption.language.label }}</option> | ||
33 | </select> | ||
34 | </div> | ||
35 | |||
36 | <input #urlInput (click)="urlInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getLink()" /> | 26 | <input #urlInput (click)="urlInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getLink()" /> |
37 | <div class="input-group-append" *ngIf="!isConfidentialVideo()"> | 27 | <div class="input-group-append" *ngIf="!isConfidentialVideo()"> |
38 | <button [cdkCopyToClipboard]="urlInput.value" (click)="activateCopiedMessage()" type="button" class="btn btn-outline-secondary"> | 28 | <button [cdkCopyToClipboard]="urlInput.value" (click)="activateCopiedMessage()" type="button" class="btn btn-outline-secondary"> |
39 | <span class="glyphicon glyphicon-copy"></span> | 29 | <span class="glyphicon glyphicon-duplicate"></span> |
40 | </button> | 30 | </button> |
41 | </div> | 31 | </div> |
42 | </div> | 32 | </div> |
43 | </div> | 33 | </ng-container> |
44 | 34 | ||
45 | <ng-container *ngIf="type === 'video' && videoFile?.metadata"> | 35 | <ng-container *ngIf="type === 'video'"> |
46 | <div ngbNav #nav="ngbNav" class="nav-tabs"> | 36 | <div ngbNav #resolutionNav="ngbNav" class="nav-tabs" [activeId]="resolutionId" (activeIdChange)="onResolutionIdChange($event)"> |
47 | 37 | ||
48 | <ng-container ngbNavItem> | 38 | <ng-container *ngFor="let file of getVideoFiles()" [ngbNavItem]="file.resolution.id"> |
49 | <a ngbNavLink i18n>Format</a> | 39 | <a ngbNavLink i18n>{{ file.resolution.label }}</a> |
40 | |||
50 | <ng-template ngbNavContent> | 41 | <ng-template ngbNavContent> |
51 | <div class="file-metadata"> | 42 | <div class="nav-content"> |
52 | <div class="metadata-attribute metadata-attribute-tags" *ngFor="let item of videoFileMetadataFormat | keyvalue"> | 43 | <div class="input-group input-group-sm"> |
53 | <span i18n class="metadata-attribute-label">{{ item.value.label }}</span> | 44 | <input #urlInput (click)="urlInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getLink()" /> |
54 | <span class="metadata-attribute-value">{{ item.value.value }}</span> | 45 | <div class="input-group-append" *ngIf="!isConfidentialVideo()"> |
46 | <button [cdkCopyToClipboard]="urlInput.value" (click)="activateCopiedMessage()" type="button" class="btn btn-outline-secondary"> | ||
47 | <span class="glyphicon glyphicon-duplicate"></span> | ||
48 | </button> | ||
49 | </div> | ||
55 | </div> | 50 | </div> |
56 | </div> | 51 | </div> |
57 | </ng-template> | 52 | </ng-template> |
58 | </ng-container> | 53 | </ng-container> |
59 | 54 | </div> | |
60 | <ng-container ngbNavItem [disabled]="videoFileMetadataVideoStream === undefined"> | 55 | <div [ngbNavOutlet]="resolutionNav"></div> |
61 | <a ngbNavLink i18n>Video stream</a> | 56 | |
62 | <ng-template ngbNavContent> | 57 | <div class="advanced-filters collapse-transition" [ngbCollapse]="isAdvancedCustomizationCollapsed"> |
63 | <div class="file-metadata"> | 58 | <ng-container *ngIf="videoFile?.metadata"> |
64 | <div class="metadata-attribute metadata-attribute-tags" *ngFor="let item of videoFileMetadataVideoStream | keyvalue"> | 59 | <div ngbNav #nav="ngbNav" class="nav-tabs nav-metadata"> |
65 | <span i18n class="metadata-attribute-label">{{ item.value.label }}</span> | 60 | <ng-container ngbNavItem> |
66 | <span class="metadata-attribute-value">{{ item.value.value }}</span> | 61 | <a ngbNavLink i18n>Format</a> |
67 | </div> | 62 | <ng-template ngbNavContent> |
63 | <div class="file-metadata"> | ||
64 | <div class="metadata-attribute metadata-attribute-tags" *ngFor="let item of videoFileMetadataFormat | keyvalue"> | ||
65 | <span i18n class="metadata-attribute-label">{{ item.value.label }}</span> | ||
66 | <span class="metadata-attribute-value">{{ item.value.value }}</span> | ||
67 | </div> | ||
68 | </div> | ||
69 | </ng-template> | ||
70 | |||
71 | <ng-container ngbNavItem [disabled]="videoFileMetadataVideoStream === undefined"> | ||
72 | <a ngbNavLink i18n>Video stream</a> | ||
73 | <ng-template ngbNavContent> | ||
74 | <div class="file-metadata"> | ||
75 | <div class="metadata-attribute metadata-attribute-tags" *ngFor="let item of videoFileMetadataVideoStream | keyvalue"> | ||
76 | <span i18n class="metadata-attribute-label">{{ item.value.label }}</span> | ||
77 | <span class="metadata-attribute-value">{{ item.value.value }}</span> | ||
78 | </div> | ||
79 | </div> | ||
80 | </ng-template> | ||
81 | </ng-container> | ||
82 | |||
83 | <ng-container ngbNavItem [disabled]="videoFileMetadataAudioStream === undefined"> | ||
84 | <a ngbNavLink i18n>Audio stream</a> | ||
85 | <ng-template ngbNavContent> | ||
86 | <div class="file-metadata"> | ||
87 | <div class="metadata-attribute metadata-attribute-tags" *ngFor="let item of videoFileMetadataAudioStream | keyvalue"> | ||
88 | <span i18n class="metadata-attribute-label">{{ item.value.label }}</span> | ||
89 | <span class="metadata-attribute-value">{{ item.value.value }}</span> | ||
90 | </div> | ||
91 | </div> | ||
92 | </ng-template> | ||
93 | </ng-container> | ||
94 | |||
95 | </ng-container> | ||
96 | </div> | ||
97 | <div [ngbNavOutlet]="nav"></div> | ||
98 | <div class="download-type"> | ||
99 | <div class="peertube-radio-container"> | ||
100 | <input type="radio" name="download" id="download-direct" [(ngModel)]="downloadType" value="direct"> | ||
101 | <label i18n for="download-direct">Direct download</label> | ||
68 | </div> | 102 | </div> |
69 | </ng-template> | 103 | <div class="peertube-radio-container"> |
70 | </ng-container> | 104 | <input type="radio" name="download" id="download-torrent" [(ngModel)]="downloadType" value="torrent"> |
71 | 105 | <label i18n for="download-torrent">Torrent (.torrent file)</label> | |
72 | <ng-container ngbNavItem [disabled]="videoFileMetadataAudioStream === undefined"> | ||
73 | <a ngbNavLink i18n>Audio stream</a> | ||
74 | <ng-template ngbNavContent> | ||
75 | <div class="file-metadata"> | ||
76 | <div class="metadata-attribute metadata-attribute-tags" *ngFor="let item of videoFileMetadataAudioStream | keyvalue"> | ||
77 | <span i18n class="metadata-attribute-label">{{ item.value.label }}</span> | ||
78 | <span class="metadata-attribute-value">{{ item.value.value }}</span> | ||
79 | </div> | ||
80 | </div> | 106 | </div> |
81 | </ng-template> | 107 | </div> |
82 | </ng-container> | 108 | </ng-container> |
83 | </div> | 109 | </div> |
84 | 110 | ||
85 | <div [ngbNavOutlet]="nav"></div> | 111 | <div (click)="isAdvancedCustomizationCollapsed = !isAdvancedCustomizationCollapsed" role="button" class="advanced-filters-button" |
86 | </ng-container> | 112 | [attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic"> |
87 | 113 | <ng-container *ngIf="isAdvancedCustomizationCollapsed"> | |
88 | <div class="download-type" *ngIf="type === 'video'"> | 114 | <span class="glyphicon glyphicon-menu-down"></span> |
89 | <div class="peertube-radio-container"> | 115 | |
90 | <input type="radio" name="download" id="download-direct" [(ngModel)]="downloadType" value="direct"> | 116 | <ng-container i18n> |
91 | <label i18n for="download-direct">Direct download</label> | 117 | Advanced |
92 | </div> | 118 | </ng-container> |
93 | 119 | </ng-container> | |
94 | <div class="peertube-radio-container"> | 120 | |
95 | <input type="radio" name="download" id="download-torrent" [(ngModel)]="downloadType" value="torrent"> | 121 | <ng-container *ngIf="!isAdvancedCustomizationCollapsed"> |
96 | <label i18n for="download-torrent">Torrent (.torrent file)</label> | 122 | <span class="glyphicon glyphicon-menu-up"></span> |
123 | |||
124 | <ng-container i18n> | ||
125 | Simple | ||
126 | </ng-container> | ||
127 | </ng-container> | ||
97 | </div> | 128 | </div> |
98 | </div> | 129 | </ng-container> |
99 | </div> | 130 | </div> |
100 | 131 | ||
101 | <div class="modal-footer inputs"> | 132 | <div class="modal-footer inputs"> |
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.scss b/client/src/app/shared/shared-video-miniature/video-download.component.scss index d407e9531..199c3dac8 100644 --- a/client/src/app/shared/shared-video-miniature/video-download.component.scss +++ b/client/src/app/shared/shared-video-miniature/video-download.component.scss | |||
@@ -1,6 +1,28 @@ | |||
1 | @import 'variables'; | 1 | @import 'variables'; |
2 | @import 'mixins'; | 2 | @import 'mixins'; |
3 | 3 | ||
4 | .nav-content { | ||
5 | margin-top: 30px; | ||
6 | } | ||
7 | |||
8 | .advanced-filters-button { | ||
9 | display: flex; | ||
10 | justify-content: center; | ||
11 | align-items: center; | ||
12 | margin-top: 20px; | ||
13 | font-size: 16px; | ||
14 | font-weight: 600; | ||
15 | cursor: pointer; | ||
16 | |||
17 | .nav-tabs { | ||
18 | margin-top: 10x; | ||
19 | } | ||
20 | |||
21 | .glyphicon { | ||
22 | margin-right: 5px; | ||
23 | } | ||
24 | } | ||
25 | |||
4 | .peertube-select-container { | 26 | .peertube-select-container { |
5 | @include peertube-select-container(85px); | 27 | @include peertube-select-container(85px); |
6 | 28 | ||
@@ -15,12 +37,21 @@ | |||
15 | } | 37 | } |
16 | } | 38 | } |
17 | 39 | ||
40 | .action-button-cancel { | ||
41 | @include peertube-button-link; | ||
42 | } | ||
43 | |||
44 | .action-button-submit { | ||
45 | @include peertube-button-link; | ||
46 | @include orange-button; | ||
47 | } | ||
48 | |||
18 | #dropdownDownloadType { | 49 | #dropdownDownloadType { |
19 | cursor: pointer; | 50 | cursor: pointer; |
20 | } | 51 | } |
21 | 52 | ||
22 | .download-type { | 53 | .download-type { |
23 | margin-top: 30px; | 54 | margin-top: 20px; |
24 | 55 | ||
25 | .peertube-radio-container { | 56 | .peertube-radio-container { |
26 | @include peertube-radio-container; | 57 | @include peertube-radio-container; |
@@ -30,6 +61,10 @@ | |||
30 | } | 61 | } |
31 | } | 62 | } |
32 | 63 | ||
64 | .nav-metadata { | ||
65 | margin-top: 20px; | ||
66 | } | ||
67 | |||
33 | .file-metadata { | 68 | .file-metadata { |
34 | padding: 1rem; | 69 | padding: 1rem; |
35 | } | 70 | } |
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.ts b/client/src/app/shared/shared-video-miniature/video-download.component.ts index 90f4daf7c..1e3745d94 100644 --- a/client/src/app/shared/shared-video-miniature/video-download.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-download.component.ts | |||
@@ -1,7 +1,9 @@ | |||
1 | import { mapValues, pick } from 'lodash-es' | 1 | import { mapValues, pick } from 'lodash-es' |
2 | import { pipe } from 'rxjs' | ||
3 | import { tap } from 'rxjs/operators' | ||
2 | import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' | 4 | import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' |
3 | import { AuthService, Notifier } from '@app/core' | 5 | import { AuthService, HooksService, Notifier } from '@app/core' |
4 | import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap' | 6 | import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' |
5 | import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' | 7 | import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' |
6 | import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoService } from '../shared-main' | 8 | import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoService } from '../shared-main' |
7 | 9 | ||
@@ -16,7 +18,7 @@ type FileMetadata = { [key: string]: { label: string, value: string }} | |||
16 | export class VideoDownloadComponent { | 18 | export class VideoDownloadComponent { |
17 | @ViewChild('modal', { static: true }) modal: ElementRef | 19 | @ViewChild('modal', { static: true }) modal: ElementRef |
18 | 20 | ||
19 | downloadType: 'direct' | 'torrent' = 'torrent' | 21 | downloadType: 'direct' | 'torrent' = 'direct' |
20 | resolutionId: number | string = -1 | 22 | resolutionId: number | string = -1 |
21 | subtitleLanguageId: string | 23 | subtitleLanguageId: string |
22 | 24 | ||
@@ -26,7 +28,9 @@ export class VideoDownloadComponent { | |||
26 | videoFileMetadataVideoStream: FileMetadata | undefined | 28 | videoFileMetadataVideoStream: FileMetadata | undefined |
27 | videoFileMetadataAudioStream: FileMetadata | undefined | 29 | videoFileMetadataAudioStream: FileMetadata | undefined |
28 | videoCaptions: VideoCaption[] | 30 | videoCaptions: VideoCaption[] |
29 | activeModal: NgbActiveModal | 31 | activeModal: NgbModalRef |
32 | |||
33 | isAdvancedCustomizationCollapsed = true | ||
30 | 34 | ||
31 | type: DownloadType = 'video' | 35 | type: DownloadType = 'video' |
32 | 36 | ||
@@ -38,7 +42,8 @@ export class VideoDownloadComponent { | |||
38 | private notifier: Notifier, | 42 | private notifier: Notifier, |
39 | private modalService: NgbModal, | 43 | private modalService: NgbModal, |
40 | private videoService: VideoService, | 44 | private videoService: VideoService, |
41 | private auth: AuthService | 45 | private auth: AuthService, |
46 | private hooks: HooksService | ||
42 | ) { | 47 | ) { |
43 | this.bytesPipe = new BytesPipe() | 48 | this.bytesPipe = new BytesPipe() |
44 | this.numbersPipe = new NumberFormatterPipe(this.localeId) | 49 | this.numbersPipe = new NumberFormatterPipe(this.localeId) |
@@ -62,9 +67,13 @@ export class VideoDownloadComponent { | |||
62 | 67 | ||
63 | this.activeModal = this.modalService.open(this.modal, { centered: true }) | 68 | this.activeModal = this.modalService.open(this.modal, { centered: true }) |
64 | 69 | ||
65 | this.resolutionId = this.getVideoFiles()[0].resolution.id | 70 | this.onResolutionIdChange(this.getVideoFiles()[0].resolution.id) |
66 | this.onResolutionIdChange() | 71 | |
67 | if (this.videoCaptions) this.subtitleLanguageId = this.videoCaptions[0].language.id | 72 | if (this.videoCaptions) this.subtitleLanguageId = this.videoCaptions[0].language.id |
73 | |||
74 | this.activeModal.shown.subscribe(() => { | ||
75 | this.hooks.runAction('action:modal.video-download.shown', 'common') | ||
76 | }) | ||
68 | } | 77 | } |
69 | 78 | ||
70 | onClose () { | 79 | onClose () { |
@@ -83,11 +92,15 @@ export class VideoDownloadComponent { | |||
83 | : this.getVideoFileLink() | 92 | : this.getVideoFileLink() |
84 | } | 93 | } |
85 | 94 | ||
86 | async onResolutionIdChange () { | 95 | async onResolutionIdChange (resolutionId: number) { |
96 | this.resolutionId = resolutionId | ||
87 | this.videoFile = this.getVideoFile() | 97 | this.videoFile = this.getVideoFile() |
88 | if (this.videoFile.metadata || !this.videoFile.metadataUrl) return | ||
89 | 98 | ||
90 | await this.hydrateMetadataFromMetadataUrl(this.videoFile) | 99 | if (!this.videoFile.metadata) { |
100 | if (!this.videoFile.metadataUrl) return | ||
101 | |||
102 | await this.hydrateMetadataFromMetadataUrl(this.videoFile) | ||
103 | } | ||
91 | 104 | ||
92 | this.videoFileMetadataFormat = this.videoFile | 105 | this.videoFileMetadataFormat = this.videoFile |
93 | ? this.getMetadataFormat(this.videoFile.metadata.format) | 106 | ? this.getMetadataFormat(this.videoFile.metadata.format) |
@@ -101,9 +114,6 @@ export class VideoDownloadComponent { | |||
101 | } | 114 | } |
102 | 115 | ||
103 | getVideoFile () { | 116 | getVideoFile () { |
104 | // HTML select send us a string, so convert it to a number | ||
105 | this.resolutionId = parseInt(this.resolutionId.toString(), 10) | ||
106 | |||
107 | const file = this.getVideoFiles().find(f => f.resolution.id === this.resolutionId) | 117 | const file = this.getVideoFiles().find(f => f.resolution.id === this.resolutionId) |
108 | if (!file) { | 118 | if (!file) { |
109 | console.error('Could not find file with resolution %d.', this.resolutionId) | 119 | console.error('Could not find file with resolution %d.', this.resolutionId) |
@@ -201,7 +211,7 @@ export class VideoDownloadComponent { | |||
201 | 211 | ||
202 | private hydrateMetadataFromMetadataUrl (file: VideoFile) { | 212 | private hydrateMetadataFromMetadataUrl (file: VideoFile) { |
203 | const observable = this.videoService.getVideoFileMetadata(file.metadataUrl) | 213 | const observable = this.videoService.getVideoFileMetadata(file.metadataUrl) |
204 | observable.subscribe(res => file.metadata = res) | 214 | .pipe(tap(res => file.metadata = res)) |
205 | 215 | ||
206 | return observable.toPromise() | 216 | return observable.toPromise() |
207 | } | 217 | } |
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.html b/client/src/app/shared/shared-video-miniature/video-miniature.component.html index 7a6df7b64..bac8bcc2d 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.html +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.html | |||
@@ -1,4 +1,4 @@ | |||
1 | <div class="video-miniature" [ngClass]="{ 'display-as-row': displayAsRow, 'fit-width': fitWidth }" (mouseenter)="loadActions()"> | 1 | <div class="video-miniature" [ngClass]="getClasses()" (mouseenter)="loadActions()"> |
2 | <my-video-thumbnail | 2 | <my-video-thumbnail |
3 | [video]="video" [nsfw]="isVideoBlur" [videoRouterLink]="videoRouterLink" [videoHref]="videoHref" [videoTarget]="videoTarget" | 3 | [video]="video" [nsfw]="isVideoBlur" [videoRouterLink]="videoRouterLink" [videoHref]="videoHref" [videoTarget]="videoTarget" |
4 | [displayWatchLaterPlaylist]="isWatchLaterPlaylistDisplayed()" [inWatchLaterPlaylist]="inWatchLaterPlaylist" (watchLaterClick)="onWatchLaterClick($event)" | 4 | [displayWatchLaterPlaylist]="isWatchLaterPlaylistDisplayed()" [inWatchLaterPlaylist]="inWatchLaterPlaylist" (watchLaterClick)="onWatchLaterClick($event)" |
@@ -9,9 +9,9 @@ | |||
9 | 9 | ||
10 | <div class="video-bottom"> | 10 | <div class="video-bottom"> |
11 | <div class="video-miniature-information"> | 11 | <div class="video-miniature-information"> |
12 | <div class="d-inline-flex video-miniature-meta"> | 12 | <div class="d-flex video-miniature-meta"> |
13 | <a *ngIf="displayOptions.avatar" class="avatar" [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle"> | 13 | <a *ngIf="displayOptions.avatar" class="avatar" [routerLink]="[ '/video-channels', video.byVideoChannel ]" [title]="channelLinkTitle"> |
14 | <img [src]="getAvatarUrl()" alt="" /> | 14 | <img [src]="getAvatarUrl()" alt="" [ngClass]="{ channel: displayOwnerVideoChannel() }" /> |
15 | </a> | 15 | </a> |
16 | 16 | ||
17 | <div class="w-100 d-flex flex-column"> | 17 | <div class="w-100 d-flex flex-column"> |
@@ -33,7 +33,7 @@ | |||
33 | </span> | 33 | </span> |
34 | </span> | 34 | </span> |
35 | 35 | ||
36 | <a tabindex="-1" *ngIf="displayOptions.by && displayOwnerAccount()" class="video-miniature-account" [routerLink]="[ '/accounts', video.byAccount ]"> | 36 | <a tabindex="-1" *ngIf="displayOptions.by && displayOwnerAccount()" class="video-miniature-account" [routerLink]="[ '/video-channels', video.byVideoChannel ]"> |
37 | {{ video.byAccount }} | 37 | {{ video.byAccount }} |
38 | </a> | 38 | </a> |
39 | <a tabindex="-1" *ngIf="displayOptions.by && displayOwnerVideoChannel()" class="video-miniature-channel" [routerLink]="[ '/video-channels', video.byVideoChannel ]"> | 39 | <a tabindex="-1" *ngIf="displayOptions.by && displayOwnerVideoChannel()" class="video-miniature-channel" [routerLink]="[ '/video-channels', video.byVideoChannel ]"> |
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.scss b/client/src/app/shared/shared-video-miniature/video-miniature.component.scss index 38cac5b6e..621951919 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.scss +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.scss | |||
@@ -3,198 +3,205 @@ | |||
3 | @import '_miniature'; | 3 | @import '_miniature'; |
4 | 4 | ||
5 | $more-button-width: 40px; | 5 | $more-button-width: 40px; |
6 | $more-margin-right: 15px; | ||
7 | 6 | ||
8 | .video-miniature { | 7 | .video-miniature-name { |
9 | display: inline-flex; | 8 | @include miniature-name; |
10 | flex-direction: column; | 9 | } |
11 | padding-bottom: $video-miniature-margin-bottom; | ||
12 | vertical-align: top; | ||
13 | 10 | ||
14 | .video-bottom { | 11 | .video-miniature-information { |
15 | display: flex; | 12 | width: calc(100% - #{$more-button-width}); |
13 | } | ||
16 | 14 | ||
17 | .video-miniature-information { | 15 | .avatar { |
18 | width: $video-miniature-width - $more-button-width - $more-margin-right; | 16 | margin: 10px 10px 0 0; |
19 | line-height: normal; | ||
20 | 17 | ||
21 | .avatar { | 18 | img:not(.channel) { |
22 | margin: 10px 10px 0 0; | 19 | @include avatar(40px); |
20 | } | ||
23 | 21 | ||
24 | img { | 22 | img.channel { |
25 | @include avatar(40px); | 23 | @include channel-avatar(40px); |
26 | } | 24 | } |
27 | } | 25 | } |
28 | 26 | ||
29 | .video-miniature-name { | 27 | .video-miniature-created-at-views { |
30 | @include miniature-name; | 28 | font-size: 13px; |
31 | width: calc(100% - #{$more-button-width}); | 29 | } |
32 | } | ||
33 | 30 | ||
34 | .video-miniature-meta { | 31 | .video-miniature-account, |
35 | width: calc(100% + #{$more-button-width}); | 32 | .video-miniature-channel { |
36 | overflow: hidden; | 33 | @include disable-default-a-behaviour; |
37 | } | 34 | @include ellipsis; |
38 | 35 | ||
39 | .video-miniature-created-at-views { | 36 | display: block; |
40 | display: block; | 37 | font-size: 13px; |
41 | font-size: 13px; | 38 | color: pvar(--greyForegroundColor); |
42 | } | ||
43 | 39 | ||
44 | .video-miniature-account, | 40 | &:hover { |
45 | .video-miniature-channel { | 41 | color: $grey-foreground-hover-color; |
46 | @include disable-default-a-behaviour; | 42 | } |
47 | @include ellipsis; | 43 | } |
48 | 44 | ||
49 | display: block; | 45 | .video-info-privacy, |
50 | font-size: 13px; | 46 | .video-info-blocked .blocked-label, |
51 | color: pvar(--greyForegroundColor); | 47 | .video-info-nsfw { |
48 | font-weight: $font-semibold; | ||
49 | } | ||
52 | 50 | ||
53 | &:hover { | 51 | .video-info-blocked { |
54 | color: $grey-foreground-hover-color; | 52 | color: red; |
55 | } | ||
56 | } | ||
57 | 53 | ||
58 | .video-info-privacy, | 54 | .blocked-reason::before { |
59 | .video-info-blocked .blocked-label, | 55 | content: ' - '; |
60 | .video-info-nsfw { | 56 | } |
61 | font-weight: $font-semibold; | 57 | } |
62 | } | ||
63 | 58 | ||
64 | .video-info-blocked { | 59 | .video-info-nsfw { |
65 | color: red; | 60 | color: red; |
61 | } | ||
66 | 62 | ||
67 | .blocked-reason::before { | 63 | .video-actions { |
68 | content: ' - '; | 64 | width: $more-button-width; |
69 | } | 65 | height: 30px; |
70 | } | ||
71 | 66 | ||
72 | .video-info-nsfw { | 67 | ::ng-deep .dropdown-root:not(.show) { |
73 | color: red; | 68 | opacity: 0; |
74 | } | 69 | } |
75 | } | ||
76 | 70 | ||
77 | .video-actions { | 71 | ::ng-deep .playlist-dropdown.show + my-action-dropdown .dropdown-root { |
78 | margin-top: 3px; | 72 | opacity: 1; |
79 | width: $more-button-width; | 73 | } |
80 | height: 30px; | ||
81 | 74 | ||
82 | ::ng-deep .dropdown-root:not(.show) { | 75 | ::ng-deep .more-icon { |
83 | opacity: 0; | 76 | opacity: .6; |
84 | } | ||
85 | 77 | ||
86 | ::ng-deep .playlist-dropdown.show + my-action-dropdown .dropdown-root { | 78 | &:hover { |
87 | opacity: 1; | 79 | opacity: 1; |
88 | } | 80 | } |
81 | } | ||
82 | } | ||
89 | 83 | ||
90 | ::ng-deep .more-icon { | 84 | .video-miniature { |
91 | opacity: .6; | 85 | &:hover ::ng-deep .video-thumbnail-actions-overlay, |
86 | &:hover .video-actions ::ng-deep .dropdown-root { | ||
87 | opacity: 1 !important; | ||
88 | } | ||
89 | } | ||
92 | 90 | ||
93 | &:hover { | 91 | // Grid mode |
94 | opacity: 1; | 92 | // Takes all the width on mobile |
95 | } | 93 | .video-miniature:not(.display-as-row) { |
96 | } | 94 | display: flex; |
97 | } | 95 | flex-direction: column; |
96 | padding-bottom: $video-miniature-margin-bottom; | ||
97 | width: 100%; | ||
98 | 98 | ||
99 | @media screen and (max-width: $small-view) { | 99 | my-video-thumbnail { |
100 | .video-miniature-information { | 100 | @include block-ratio($selector: '::ng-deep .video-thumbnail'); |
101 | margin: 0 10px; | 101 | } |
102 | } | ||
103 | 102 | ||
104 | .video-actions { | 103 | .video-bottom { |
105 | margin: 0; | 104 | display: flex; |
106 | top: -3px; | 105 | width: 100%; |
106 | } | ||
107 | 107 | ||
108 | ::ng-deep .dropdown-root { | 108 | .video-miniature-name { |
109 | opacity: 1 !important; | 109 | margin-top: 10px; |
110 | } | 110 | margin-bottom: 5px; |
111 | } | ||
112 | } | ||
113 | } | 111 | } |
114 | 112 | ||
115 | &:hover ::ng-deep .video-thumbnail .video-thumbnail-actions-overlay, | 113 | .video-miniature-created-at-views { |
116 | &:hover .video-bottom .video-actions ::ng-deep .dropdown-root { | 114 | display: block; |
117 | opacity: 1; | ||
118 | } | 115 | } |
119 | 116 | ||
120 | &.fit-width { | 117 | .video-actions { |
118 | margin-top: 3px; | ||
119 | } | ||
120 | |||
121 | @media screen and (max-width: $small-view) { | ||
121 | width: 100%; | 122 | width: 100%; |
123 | margin-bottom: 25px; | ||
124 | |||
125 | .video-miniature-information { | ||
126 | margin: 0 10px; | ||
127 | |||
128 | width: 100%; | ||
129 | text-align: left; | ||
130 | } | ||
122 | 131 | ||
123 | .video-bottom { | 132 | .video-actions { |
124 | width: 100% !important; | 133 | margin: 0; |
134 | top: -3px; | ||
125 | 135 | ||
126 | .video-miniature-information { | 136 | ::ng-deep .dropdown-root { |
127 | width: calc(100% - #{$more-button-width}) !important; | 137 | opacity: 1 !important; |
128 | } | 138 | } |
129 | } | 139 | } |
130 | 140 | ||
131 | my-video-thumbnail { | 141 | ::ng-deep .video-thumbnail { |
132 | @include large-screen-ratio($selector: '::ng-deep .video-thumbnail'); | 142 | border-radius: 0; |
133 | } | 143 | } |
134 | } | 144 | } |
145 | } | ||
146 | |||
147 | .video-miniature.display-as-row { | ||
148 | --rowThumbnailWidth: #{$video-thumbnail-width}; | ||
149 | --rowThumbnailHeight: #{$video-thumbnail-height}; | ||
150 | |||
151 | display: flex; | ||
152 | flex-direction: row; | ||
135 | 153 | ||
136 | &.display-as-row { | 154 | .video-bottom { |
137 | flex-direction: row; | ||
138 | padding-bottom: 0; | ||
139 | height: auto; | ||
140 | display: flex; | 155 | display: flex; |
141 | flex-grow: 1; | 156 | } |
142 | 157 | ||
143 | my-video-thumbnail { | 158 | // We don't display avatar in row mode |
144 | margin-right: 10px; | 159 | .avatar { |
145 | } | 160 | display: none; |
161 | } | ||
146 | 162 | ||
147 | .video-bottom { | 163 | my-video-thumbnail { |
148 | .video-miniature-information { | 164 | min-width: var(--rowThumbnailWidth); |
149 | @media screen and (min-width: $small-view) { | 165 | max-width: var(--rowThumbnailWidth); |
150 | width: auto; | 166 | height: var(--rowThumbnailHeight); |
151 | min-width: 500px; | 167 | margin-right: 10px; |
152 | } | 168 | } |
153 | |||
154 | .video-miniature-name { | ||
155 | @include ellipsis-multiline(1.3em, 2); | ||
156 | |||
157 | margin-top: 2px; | ||
158 | margin-bottom: 5px; | ||
159 | } | ||
160 | |||
161 | .video-miniature-created-at-views, | ||
162 | .video-miniature-account, | ||
163 | .video-miniature-channel { | ||
164 | font-size: 95%; | ||
165 | width: fit-content; | ||
166 | } | ||
167 | |||
168 | .video-miniature-created-at-views + .video-miniature-channel { | ||
169 | margin-top: 5px; | ||
170 | } | ||
171 | |||
172 | .video-info-privacy { | ||
173 | margin-top: 5px; | ||
174 | } | ||
175 | |||
176 | .video-info-blocked { | ||
177 | margin-top: 3px; | ||
178 | } | ||
179 | } | ||
180 | 169 | ||
181 | .video-actions { | 170 | .video-miniature-name { |
182 | margin: 0; | 171 | @include ellipsis-multiline($video-miniature-row-name-font-size, 2); |
183 | top: -3px; | 172 | } |
184 | } | 173 | |
185 | } | 174 | .video-miniature-created-at-views, |
175 | .video-miniature-account, | ||
176 | .video-miniature-channel { | ||
177 | font-size: $video-miniature-row-info-font-size; | ||
178 | } | ||
186 | 179 | ||
187 | @media screen and (max-width: $small-view) { | 180 | .video-actions { |
188 | flex-direction: column; | 181 | margin-top: -3px; |
189 | height: auto; | 182 | } |
183 | } | ||
190 | 184 | ||
191 | my-video-thumbnail { | 185 | @include on-small-main-col { |
192 | margin-right: 0; | 186 | .video-miniature.display-as-row { |
193 | } | 187 | --rowThumbnailWidth: #{$video-thumbnail-medium-width}; |
188 | --rowThumbnailHeight: #{$video-thumbnail-medium-height}; | ||
189 | } | ||
190 | } | ||
194 | 191 | ||
195 | .video-miniature-information { | 192 | @include on-mobile-main-col { |
196 | min-width: initial; | 193 | .video-miniature.display-as-row { |
197 | } | 194 | --rowThumbnailWidth: #{$video-thumbnail-small-width}; |
195 | --rowThumbnailHeight: #{$video-thumbnail-small-height}; | ||
196 | |||
197 | .video-miniature-name { | ||
198 | font-size: $video-miniature-row-info-font-size; | ||
199 | } | ||
200 | |||
201 | .video-miniature-created-at-views, | ||
202 | .video-miniature-account, | ||
203 | .video-miniature-channel { | ||
204 | font-size: $video-miniature-row-mobile-info-font-size; | ||
198 | } | 205 | } |
199 | } | 206 | } |
200 | } | 207 | } |
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts index cc5665ab1..48da92d6b 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts | |||
@@ -16,7 +16,6 @@ import { Video } from '../shared-main' | |||
16 | import { VideoPlaylistService } from '../shared-video-playlist' | 16 | import { VideoPlaylistService } from '../shared-video-playlist' |
17 | import { VideoActionsDisplayType } from './video-actions-dropdown.component' | 17 | import { VideoActionsDisplayType } from './video-actions-dropdown.component' |
18 | 18 | ||
19 | export type OwnerDisplayType = 'account' | 'videoChannel' | 'auto' | ||
20 | export type MiniatureDisplayOptions = { | 19 | export type MiniatureDisplayOptions = { |
21 | date?: boolean | 20 | date?: boolean |
22 | views?: boolean | 21 | views?: boolean |
@@ -40,7 +39,6 @@ export class VideoMiniatureComponent implements OnInit { | |||
40 | @Input() user: User | 39 | @Input() user: User |
41 | @Input() video: Video | 40 | @Input() video: Video |
42 | 41 | ||
43 | @Input() ownerDisplayType: OwnerDisplayType = 'account' | ||
44 | @Input() displayOptions: MiniatureDisplayOptions = { | 42 | @Input() displayOptions: MiniatureDisplayOptions = { |
45 | date: true, | 43 | date: true, |
46 | views: true, | 44 | views: true, |
@@ -51,9 +49,9 @@ export class VideoMiniatureComponent implements OnInit { | |||
51 | state: false, | 49 | state: false, |
52 | blacklistInfo: false | 50 | blacklistInfo: false |
53 | } | 51 | } |
54 | @Input() displayAsRow = false | ||
55 | @Input() displayVideoActions = true | 52 | @Input() displayVideoActions = true |
56 | @Input() fitWidth = false | 53 | |
54 | @Input() displayAsRow = false | ||
57 | 55 | ||
58 | @Input() videoLinkType: VideoLinkType = 'internal' | 56 | @Input() videoLinkType: VideoLinkType = 'internal' |
59 | 57 | ||
@@ -89,7 +87,7 @@ export class VideoMiniatureComponent implements OnInit { | |||
89 | videoHref: string | 87 | videoHref: string |
90 | videoTarget: string | 88 | videoTarget: string |
91 | 89 | ||
92 | private ownerDisplayTypeChosen: 'account' | 'videoChannel' | 90 | private ownerDisplayType: 'account' | 'videoChannel' |
93 | 91 | ||
94 | constructor ( | 92 | constructor ( |
95 | private screenService: ScreenService, | 93 | private screenService: ScreenService, |
@@ -140,11 +138,11 @@ export class VideoMiniatureComponent implements OnInit { | |||
140 | } | 138 | } |
141 | 139 | ||
142 | displayOwnerAccount () { | 140 | displayOwnerAccount () { |
143 | return this.ownerDisplayTypeChosen === 'account' | 141 | return this.ownerDisplayType === 'account' |
144 | } | 142 | } |
145 | 143 | ||
146 | displayOwnerVideoChannel () { | 144 | displayOwnerVideoChannel () { |
147 | return this.ownerDisplayTypeChosen === 'videoChannel' | 145 | return this.ownerDisplayType === 'videoChannel' |
148 | } | 146 | } |
149 | 147 | ||
150 | isUnlistedVideo () { | 148 | isUnlistedVideo () { |
@@ -183,7 +181,7 @@ export class VideoMiniatureComponent implements OnInit { | |||
183 | } | 181 | } |
184 | 182 | ||
185 | getAvatarUrl () { | 183 | getAvatarUrl () { |
186 | if (this.ownerDisplayTypeChosen === 'account') { | 184 | if (this.displayOwnerAccount()) { |
187 | return this.video.accountAvatarUrl | 185 | return this.video.accountAvatarUrl |
188 | } | 186 | } |
189 | 187 | ||
@@ -244,21 +242,26 @@ export class VideoMiniatureComponent implements OnInit { | |||
244 | return this.displayVideoActions && this.isUserLoggedIn() && this.inWatchLaterPlaylist !== undefined | 242 | return this.displayVideoActions && this.isUserLoggedIn() && this.inWatchLaterPlaylist !== undefined |
245 | } | 243 | } |
246 | 244 | ||
247 | private setUpBy () { | 245 | getClasses () { |
248 | if (this.ownerDisplayType === 'account' || this.ownerDisplayType === 'videoChannel') { | 246 | return { |
249 | this.ownerDisplayTypeChosen = this.ownerDisplayType | 247 | 'display-as-row': this.displayAsRow |
250 | return | ||
251 | } | 248 | } |
249 | } | ||
250 | |||
251 | private setUpBy () { | ||
252 | const accountName = this.video.account.name | ||
252 | 253 | ||
253 | // If the video channel name an UUID (not really displayable, we changed this behaviour in v1.0.0-beta.12) | 254 | // If the video channel name is an UUID (not really displayable, we changed this behaviour in v1.0.0-beta.12) |
255 | // Or has not been customized (default created channel display name) | ||
254 | // -> Use the account name | 256 | // -> Use the account name |
255 | if ( | 257 | if ( |
256 | this.video.channel.name === `${this.video.account.name}_channel` || | 258 | this.video.channel.displayName === `Default ${accountName} channel` || |
259 | this.video.channel.displayName === `Main ${accountName} channel` || | ||
257 | this.video.channel.name.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/) | 260 | this.video.channel.name.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/) |
258 | ) { | 261 | ) { |
259 | this.ownerDisplayTypeChosen = 'account' | 262 | this.ownerDisplayType = 'account' |
260 | } else { | 263 | } else { |
261 | this.ownerDisplayTypeChosen = 'videoChannel' | 264 | this.ownerDisplayType = 'videoChannel' |
262 | } | 265 | } |
263 | } | 266 | } |
264 | 267 | ||
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.html b/client/src/app/shared/shared-video-miniature/videos-selection.component.html index 8caeaf092..dec9e99f3 100644 --- a/client/src/app/shared/shared-video-miniature/videos-selection.component.html +++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.html | |||
@@ -9,8 +9,7 @@ | |||
9 | 9 | ||
10 | <my-video-miniature | 10 | <my-video-miniature |
11 | [video]="video" [displayAsRow]="true" [displayOptions]="miniatureDisplayOptions" | 11 | [video]="video" [displayAsRow]="true" [displayOptions]="miniatureDisplayOptions" |
12 | [displayVideoActions]="false" [ownerDisplayType]="ownerDisplayType" | 12 | [displayVideoActions]="false" [user]="user" |
13 | [user]="user" | ||
14 | ></my-video-miniature> | 13 | ></my-video-miniature> |
15 | 14 | ||
16 | <!-- Display only once --> | 15 | <!-- Display only once --> |
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.scss b/client/src/app/shared/shared-video-miniature/videos-selection.component.scss index c33e11889..a2939d521 100644 --- a/client/src/app/shared/shared-video-miniature/videos-selection.component.scss +++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.scss | |||
@@ -5,24 +5,24 @@ | |||
5 | display: flex; | 5 | display: flex; |
6 | justify-content: flex-end; | 6 | justify-content: flex-end; |
7 | flex-grow: 1; | 7 | flex-grow: 1; |
8 | } | ||
8 | 9 | ||
9 | .action-selection-mode-child { | 10 | .action-selection-mode-child { |
10 | position: fixed; | 11 | position: fixed; |
11 | |||
12 | .action-button { | ||
13 | display: block; | ||
14 | margin-left: 55px; | ||
15 | } | ||
16 | 12 | ||
17 | .action-button-cancel-selection { | 13 | .action-button { |
18 | @include peertube-button; | 14 | display: block; |
19 | @include grey-button; | 15 | margin-left: 55px; |
20 | } | ||
21 | } | 16 | } |
22 | } | 17 | } |
23 | 18 | ||
19 | .action-button-cancel-selection { | ||
20 | @include peertube-button; | ||
21 | @include grey-button; | ||
22 | } | ||
23 | |||
24 | .video { | 24 | .video { |
25 | @include row-blocks; | 25 | @include row-blocks($column-responsive: false); |
26 | 26 | ||
27 | &:first-child { | 27 | &:first-child { |
28 | margin-top: 47px; | 28 | margin-top: 47px; |
@@ -40,18 +40,16 @@ | |||
40 | } | 40 | } |
41 | } | 41 | } |
42 | 42 | ||
43 | @media screen and (max-width: $small-view) { | ||
44 | .video { | ||
45 | flex-direction: column; | ||
46 | height: auto; | ||
47 | 43 | ||
48 | .checkbox-container { | 44 | @include on-small-main-col { |
49 | display: none; | 45 | .video { |
50 | } | 46 | flex-wrap: wrap; |
47 | } | ||
48 | } | ||
51 | 49 | ||
52 | my-button { | 50 | @include on-mobile-main-col { |
53 | margin-top: 10px; | 51 | .checkbox-container { |
54 | } | 52 | display: none; |
55 | } | 53 | } |
56 | 54 | ||
57 | .action-selection-mode { | 55 | .action-selection-mode { |
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts index ca1cf2264..f8c3800d7 100644 --- a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts +++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts | |||
@@ -17,7 +17,7 @@ import { AuthService, ComponentPagination, LocalStorageService, Notifier, Screen | |||
17 | import { ResultList, VideoSortField } from '@shared/models' | 17 | import { ResultList, VideoSortField } from '@shared/models' |
18 | import { PeerTubeTemplateDirective, Video } from '../shared-main' | 18 | import { PeerTubeTemplateDirective, Video } from '../shared-main' |
19 | import { AbstractVideoList } from './abstract-video-list' | 19 | import { AbstractVideoList } from './abstract-video-list' |
20 | import { MiniatureDisplayOptions, OwnerDisplayType } from './video-miniature.component' | 20 | import { MiniatureDisplayOptions } from './video-miniature.component' |
21 | 21 | ||
22 | export type SelectionType = { [ id: number ]: boolean } | 22 | export type SelectionType = { [ id: number ]: boolean } |
23 | 23 | ||
@@ -31,7 +31,6 @@ export class VideosSelectionComponent extends AbstractVideoList implements OnIni | |||
31 | @Input() pagination: ComponentPagination | 31 | @Input() pagination: ComponentPagination |
32 | @Input() titlePage: string | 32 | @Input() titlePage: string |
33 | @Input() miniatureDisplayOptions: MiniatureDisplayOptions | 33 | @Input() miniatureDisplayOptions: MiniatureDisplayOptions |
34 | @Input() ownerDisplayType: OwnerDisplayType | ||
35 | 34 | ||
36 | @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<ResultList<Video>> | 35 | @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<ResultList<Video>> |
37 | 36 | ||
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.html b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.html index 86f6664cb..f50f95003 100644 --- a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.html +++ b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.html | |||
@@ -1,4 +1,4 @@ | |||
1 | <div class="miniature" [ngClass]="{ 'no-videos': playlist.videosLength === 0, 'to-manage': toManage }"> | 1 | <div class="miniature" [ngClass]="{ 'no-videos': playlist.videosLength === 0, 'to-manage': toManage, 'display-as-row': displayAsRow }"> |
2 | <a | 2 | <a |
3 | [routerLink]="getPlaylistUrl()" [attr.title]="playlist.description" | 3 | [routerLink]="getPlaylistUrl()" [attr.title]="playlist.description" |
4 | class="miniature-thumbnail" | 4 | class="miniature-thumbnail" |
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss index 1b16dbb01..c5be5f292 100644 --- a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss +++ b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.scss | |||
@@ -4,6 +4,7 @@ | |||
4 | 4 | ||
5 | .miniature { | 5 | .miniature { |
6 | display: inline-block; | 6 | display: inline-block; |
7 | width: 100%; | ||
7 | 8 | ||
8 | &.no-videos:not(.to-manage){ | 9 | &.no-videos:not(.to-manage){ |
9 | a { | 10 | a { |
@@ -17,62 +18,92 @@ | |||
17 | display: none; | 18 | display: none; |
18 | } | 19 | } |
19 | } | 20 | } |
21 | } | ||
20 | 22 | ||
21 | .miniature-thumbnail { | 23 | .miniature-thumbnail { |
22 | @include miniature-thumbnail; | 24 | @include miniature-thumbnail; |
23 | 25 | ||
24 | .miniature-playlist-info-overlay { | 26 | .miniature-playlist-info-overlay { |
25 | @include static-thumbnail-overlay; | 27 | @include static-thumbnail-overlay; |
26 | 28 | ||
27 | position: absolute; | 29 | position: absolute; |
28 | right: 0; | 30 | right: 0; |
29 | bottom: 0; | 31 | bottom: 0; |
30 | height: $video-thumbnail-height; | 32 | height: 100%; |
31 | padding: 0 10px; | 33 | padding: 0 10px; |
32 | display: flex; | 34 | display: flex; |
33 | align-items: center; | 35 | align-items: center; |
34 | font-size: 14px; | 36 | font-size: 14px; |
35 | font-weight: $font-semibold; | 37 | font-weight: $font-semibold; |
36 | } | ||
37 | } | 38 | } |
39 | } | ||
38 | 40 | ||
39 | .miniature-info { | 41 | .miniature-info { |
40 | width: 200px; | ||
41 | margin-top: 2px; | ||
42 | line-height: normal; | ||
43 | |||
44 | .miniature-name { | ||
45 | @include miniature-name; | ||
46 | 42 | ||
47 | @include ellipsis-multiline(1.3em, 2); | 43 | .miniature-name { |
44 | @include miniature-name; | ||
45 | @include ellipsis-multiline(1.3em, 2); | ||
48 | 46 | ||
49 | margin: 0; | 47 | margin: 0; |
50 | } | 48 | } |
51 | 49 | ||
52 | .by { | 50 | .by { |
53 | @include disable-default-a-behaviour; | 51 | @include disable-default-a-behaviour; |
54 | 52 | ||
55 | display: block; | 53 | display: block; |
56 | color: pvar(--greyForegroundColor); | 54 | color: pvar(--greyForegroundColor); |
57 | } | 55 | } |
58 | 56 | ||
59 | .privacy-date { | 57 | .privacy-date { |
60 | margin-top: 5px; | 58 | margin-top: 5px; |
61 | 59 | ||
62 | .video-info-privacy { | 60 | .video-info-privacy { |
63 | font-size: 14px; | 61 | font-size: 14px; |
64 | font-weight: $font-semibold; | 62 | font-weight: $font-semibold; |
65 | 63 | ||
66 | &::after { | 64 | &::after { |
67 | content: '-'; | 65 | content: '-'; |
68 | margin: 0 3px; | 66 | margin: 0 3px; |
69 | } | ||
70 | } | 67 | } |
71 | } | 68 | } |
69 | } | ||
72 | 70 | ||
73 | .video-info-description { | 71 | .video-info-description { |
74 | margin-top: 10px; | 72 | margin-top: 10px; |
75 | color: pvar(--greyForegroundColor); | 73 | color: pvar(--greyForegroundColor); |
76 | } | 74 | } |
75 | } | ||
76 | |||
77 | .miniature:not(.display-as-row) { | ||
78 | .miniature-thumbnail { | ||
79 | margin-top: 10px; | ||
80 | margin-bottom: 5px; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | .miniature.display-as-row { | ||
85 | --rowThumbnailWidth: #{$video-thumbnail-width}; | ||
86 | --rowThumbnailHeight: #{$video-thumbnail-height}; | ||
87 | |||
88 | display: flex; | ||
89 | |||
90 | .miniature-thumbnail { | ||
91 | width: var(--rowThumbnailWidth); | ||
92 | height: var(--rowThumbnailHeight); | ||
93 | margin-right: 10px; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | @include on-small-main-col { | ||
98 | .miniature.display-as-row { | ||
99 | --rowThumbnailWidth: #{$video-thumbnail-medium-width}; | ||
100 | --rowThumbnailHeight: #{$video-thumbnail-medium-height}; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | @include on-mobile-main-col { | ||
105 | .miniature.display-as-row { | ||
106 | --rowThumbnailWidth: #{$video-thumbnail-small-width}; | ||
107 | --rowThumbnailHeight: #{$video-thumbnail-small-height}; | ||
77 | } | 108 | } |
78 | } | 109 | } |
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.ts b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.ts index 251aa868a..6b0b1056f 100644 --- a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.ts +++ b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.ts | |||
@@ -12,6 +12,7 @@ export class VideoPlaylistMiniatureComponent { | |||
12 | @Input() displayChannel = false | 12 | @Input() displayChannel = false |
13 | @Input() displayDescription = false | 13 | @Input() displayDescription = false |
14 | @Input() displayPrivacy = false | 14 | @Input() displayPrivacy = false |
15 | @Input() displayAsRow = false | ||
15 | 16 | ||
16 | getPlaylistUrl () { | 17 | getPlaylistUrl () { |
17 | if (this.toManage) return [ '/my-library/video-playlists', this.playlist.uuid ] | 18 | if (this.toManage) return [ '/my-library/video-playlists', this.playlist.uuid ] |
diff --git a/client/src/assets/images/feather/cloud-download.svg b/client/src/assets/images/feather/cloud-download.svg index 3a4e58df1..16526d338 100644 --- a/client/src/assets/images/feather/cloud-download.svg +++ b/client/src/assets/images/feather/cloud-download.svg | |||
@@ -1,6 +1,6 @@ | |||
1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> | 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> |
2 | <defs/> | 2 | <defs/> |
3 | <g fill="none" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-width="2"> | 3 | <g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-width="2"> |
4 | <path stroke-linejoin="round" d="M8 17H5h0a4 4 0 111-7.9v-.6a5.5 5.5 0 0110.8-1.4A5 5 0 0123 12a5 5 0 01-5 5h-2"/> | 4 | <path stroke-linejoin="round" d="M8 17H5h0a4 4 0 111-7.9v-.6a5.5 5.5 0 0110.8-1.4A5 5 0 0123 12a5 5 0 01-5 5h-2"/> |
5 | <path d="M12 13v8"/> | 5 | <path d="M12 13v8"/> |
6 | <path stroke-linejoin="round" d="M15 20l-3 3-3-3"/> | 6 | <path stroke-linejoin="round" d="M15 20l-3 3-3-3"/> |
diff --git a/client/src/assets/images/feather/subscriptions.svg b/client/src/assets/images/feather/subscriptions.svg deleted file mode 100644 index c7216352a..000000000 --- a/client/src/assets/images/feather/subscriptions.svg +++ /dev/null | |||
@@ -1,19 +0,0 @@ | |||
1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> | ||
2 | <defs/> | ||
3 | <defs> | ||
4 | <linearGradient id="a" x1="50%" x2="50%" y1="0%" y2="97.33%"> | ||
5 | <stop stop-color="#000" offset="0%"/> | ||
6 | <stop stop-color="#000" offset="100%" stop-opacity=".25"/> | ||
7 | </linearGradient> | ||
8 | <linearGradient id="b" x1="50%" x2="50%" y1="0%" y2="97.86%"> | ||
9 | <stop stop-color="#000" offset="0%"/> | ||
10 | <stop stop-color="#000" offset="100%" stop-opacity=".25"/> | ||
11 | </linearGradient> | ||
12 | </defs> | ||
13 | <g fill="none" fill-rule="evenodd"> | ||
14 | <circle cx="12" cy="10" r="3" fill="#000"/> | ||
15 | <path fill="url(#a)" fill-rule="nonzero" d="M16.39 13.85A5.68 5.68 0 0018 10c0-3.26-2.74-6-6-6s-6 2.74-6 6c0 1.42.58 2.7 1.62 3.85a.5.5 0 00.74-.67A4.7 4.7 0 017 10c0-2.7 2.3-5 5-5s5 2.3 5 5a4.7 4.7 0 01-1.36 3.18.5.5 0 10.75.67z"/> | ||
16 | <path fill="url(#b)" fill-rule="nonzero" d="M17.57 18.3A9.99 9.99 0 0012 0a10 10 0 00-5.56 18.31 1 1 0 101.11-1.66 7.99 7.99 0 118.9 0 1 1 0 101.12 1.66z"/> | ||
17 | <path fill="#000" d="M9.33 15.98A1.64 1.64 0 0111 14h2c1.1 0 1.85.88 1.67 1.98l-1 6.04c-.1.54-.61.98-1.17.98h-1c-.55 0-1.07-.43-1.16-.98l-1.01-6.04z"/> | ||
18 | </g> | ||
19 | </svg> | ||
diff --git a/client/src/assets/images/misc/language.svg b/client/src/assets/images/misc/language.svg index 8fd1d0ba8..204136f0b 100644 --- a/client/src/assets/images/misc/language.svg +++ b/client/src/assets/images/misc/language.svg | |||
@@ -1,7 +1,7 @@ | |||
1 | <svg xmlns="http://www.w3.org/2000/svg" transform="scale(1.2)" viewBox="0 0 200 200"> | 1 | <svg xmlns="http://www.w3.org/2000/svg" transform="scale(1.2)" viewBox="0 0 200 200"> |
2 | <defs/> | 2 | <defs/> |
3 | <path stroke="#000" stroke-width="3" d="M93 155H42a18 18 0 01-18-18V29a5 5 0 015-5h89a5 5 0 015 6L98 151a5 5 0 01-5 4zM34 34v103a8 8 0 008 8h47l22-111z"/> | 3 | <path stroke="currentColor" stroke-width="3" d="M93 155H42a18 18 0 01-18-18V29a5 5 0 015-5h89a5 5 0 015 6L98 151a5 5 0 01-5 4zM34 34v103a8 8 0 008 8h47l22-111z"/> |
4 | <path stroke="#000" stroke-width="3" d="M171 176H75a5 5 0 01-5-6l4-21a5 5 0 0110 2l-3 15h85V63a8 8 0 00-8-8h-45a5 5 0 010-10h45a18 18 0 0118 18v108a5 5 0 01-5 5zM50 92h0a5 5 0 01-5-5V63a17 17 0 0135 0v24a5 5 0 01-10 0V62a7 7 0 00-15 0v25a5 5 0 01-5 5z"/> | 4 | <path stroke="currentColor" stroke-width="3" d="M171 176H75a5 5 0 01-5-6l4-21a5 5 0 0110 2l-3 15h85V63a8 8 0 00-8-8h-45a5 5 0 010-10h45a18 18 0 0118 18v108a5 5 0 01-5 5zM50 92h0a5 5 0 01-5-5V63a17 17 0 0135 0v24a5 5 0 01-10 0V62a7 7 0 00-15 0v25a5 5 0 01-5 5z"/> |
5 | <path stroke="#000" stroke-width="3" d="M75 76H50a5 5 0 010-10h25a5 5 0 010 10zM120 155a5 5 0 01-3-9l21-21h-18a5 5 0 010-10h30a5 5 0 014 9l-30 30a5 5 0 01-4 1z"/> | 5 | <path stroke="currentColor" stroke-width="3" d="M75 76H50a5 5 0 010-10h25a5 5 0 010 10zM120 155a5 5 0 01-3-9l21-21h-18a5 5 0 010-10h30a5 5 0 014 9l-30 30a5 5 0 01-4 1z"/> |
6 | <path stroke="#000" stroke-width="3" d="M150 155a5 5 0 01-4-1l-14-15a5 5 0 017-7l15 14a5 5 0 01-4 9zM143 110h-15a5 5 0 110-10h15a5 5 0 010 10z"/> | 6 | <path stroke="currentColor" stroke-width="3" d="M150 155a5 5 0 01-4-1l-14-15a5 5 0 017-7l15 14a5 5 0 01-4 9zM143 110h-15a5 5 0 110-10h15a5 5 0 010 10z"/> |
7 | </svg> | 7 | </svg> |
diff --git a/client/src/assets/images/misc/npm.svg b/client/src/assets/images/misc/npm.svg index 1d1d82784..8a4869f12 100644 --- a/client/src/assets/images/misc/npm.svg +++ b/client/src/assets/images/misc/npm.svg | |||
@@ -1,5 +1,5 @@ | |||
1 | <svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 18 7" style="transform: scale(1.3) translateY(1px);"> | 1 | <svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 18 7" style="transform: scale(1.3) translateY(1px);"> |
2 | <path fill="#000" d="M0,0h18v6H9v1H5V6H0V0z M1,5h2V2h1v3h1V1H1V5z M6,1v5h2V5h2V1H6z M8,2h1v2H8V2z M11,1v4h2V2h1v3h1V2h1v3h1V1H11z"/> | 2 | <path fill="currentColor" d="M0,0h18v6H9v1H5V6H0V0z M1,5h2V2h1v3h1V1H1V5z M6,1v5h2V5h2V1H6z M8,2h1v2H8V2z M11,1v4h2V2h1v3h1V2h1v3h1V1H11z"/> |
3 | <polygon fill="#FFFFFF" points="1,5 3,5 3,2 4,2 4,5 5,5 5,1 1,1 "/> | 3 | <polygon fill="#FFFFFF" points="1,5 3,5 3,2 4,2 4,5 5,5 5,1 1,1 "/> |
4 | <polygon fill="#FFFFFF" d="M6,1v5h2V5h2V1H6z M9,4H8V2h1V4z"/> | 4 | <polygon fill="#FFFFFF" d="M6,1v5h2V5h2V1H6z M9,4H8V2h1V4z"/> |
5 | <polygon fill="#FFFFFF" points="11,1 11,5 13,5 13,2 14,2 14,5 15,5 15,2 16,2 16,5 17,5 17,1 "/> | 5 | <polygon fill="#FFFFFF" points="11,1 11,5 13,5 13,2 14,2 14,5 15,5 15,2 16,2 16,5 17,5 17,1 "/> |
diff --git a/client/src/assets/images/misc/peertube-x.svg b/client/src/assets/images/misc/peertube-x.svg index 0099e627d..30ab665e7 100644 --- a/client/src/assets/images/misc/peertube-x.svg +++ b/client/src/assets/images/misc/peertube-x.svg | |||
@@ -1,20 +1,17 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> |
2 | <!-- Generator: Adobe Illustrator 23.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> | 2 | <style type="text/css"> |
3 | <svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | 3 | .st0{fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;} |
4 | viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve"> | 4 | .st1{fill:currentColor;} |
5 | <style type="text/css"> | ||
6 | .st0{fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;} | ||
7 | .st1{fill:#211F20;} | ||
8 | </style> | 5 | </style> |
9 | <line class="st0" x1="17.1" y1="9.5" x2="22.1" y2="14.5"/> | 6 | <line class="st0" x1="17.1" y1="9.5" x2="22.1" y2="14.5" /> |
10 | <line class="st0" x1="22.1" y1="9.5" x2="17.1" y2="14.5"/> | 7 | <line class="st0" x1="22.1" y1="9.5" x2="17.1" y2="14.5" /> |
11 | <g> | ||
12 | <g> | 8 | <g> |
13 | <g> | 9 | <g> |
14 | <path class="st1" d="M2,2.6V12l6.9-4.3"/> | 10 | <g> |
15 | <path class="st1" d="M2,12v9.4l6.9-5.2"/> | 11 | <path class="st1" d="M2,2.6V12l6.9-4.3" /> |
16 | <path class="st1" d="M8.9,7.7v8.6l6.9-4.3"/> | 12 | <path class="st1" d="M2,12v9.4l6.9-5.2" /> |
13 | <path class="st1" d="M8.9,7.7v8.6l6.9-4.3" /> | ||
14 | </g> | ||
17 | </g> | 15 | </g> |
18 | </g> | 16 | </g> |
19 | </g> | ||
20 | </svg> | 17 | </svg> |
diff --git a/client/src/assets/images/misc/playlist-add.svg b/client/src/assets/images/misc/playlist-add.svg index 7ec77b851..4be495e83 100644 --- a/client/src/assets/images/misc/playlist-add.svg +++ b/client/src/assets/images/misc/playlist-add.svg | |||
@@ -1,5 +1,5 @@ | |||
1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 426.7 426.7"> | 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 426.7 426.7"> |
2 | <defs/> | 2 | <defs/> |
3 | <path fill="#000" d="M0 64h256v42.7H0zM0 149.3h256V192H0zM0 234.7h170.7v42.7H0z"/> | 3 | <path fill="currentColor" d="M0 64h256v42.7H0zM0 149.3h256V192H0zM0 234.7h170.7v42.7H0z"/> |
4 | <path fill="#000" d="M341.3 234.7v-85.4h-42.6v85.4h-85.4v42.6h85.4v85.4h42.6v-85.4h85.4v-42.6z"/> | 4 | <path fill="currentColor" d="M341.3 234.7v-85.4h-42.6v85.4h-85.4v42.6h85.4v85.4h42.6v-85.4h85.4v-42.6z"/> |
5 | </svg> | 5 | </svg> |
diff --git a/client/src/assets/images/misc/support.svg b/client/src/assets/images/misc/support.svg index 66280e18d..be3f58c24 100644 --- a/client/src/assets/images/misc/support.svg +++ b/client/src/assets/images/misc/support.svg | |||
@@ -6,9 +6,9 @@ | |||
6 | <g transform="translate(2.669496,27.625894)"> | 6 | <g transform="translate(2.669496,27.625894)"> |
7 | <g transform="matrix(0.1,0,0,-0.1,0,511)"> | 7 | <g transform="matrix(0.1,0,0,-0.1,0,511)"> |
8 | <path d="m 3744.3542,4564.3712 c -217.4,-34.2 -520.3,-200.3 -693.7,-376.2 -263.8,-263.8 -388.4,-571.6 -388.4,-952.6 0,-256.5 44,-437.2 173.4,-684 75.7,-144.1 197.9,-280.9 747.5,-842.7 1106.5,-1133.40001 1138.2,-1165.20001 1253,-1194.50001 188.1,-51.3 214.9,-29.3 1162.7,938.00001 498.3,508.1 911.1,950.2 962.4,1030.8 263.8,415.3 283.3,964.9 48.8,1409.4 -180.8,342 -581.3,620.4 -972.2,676.6 -332.2,48.9 -671.7,-36.6 -967.3,-236.9 l -156.3,-109.9 -119.7,87.9 c -158.8,117.2 -351.8,202.7 -554.5,244.3 -183.1,39.1 -295.4,41.6 -495.7,9.8 z" | 8 | <path d="m 3744.3542,4564.3712 c -217.4,-34.2 -520.3,-200.3 -693.7,-376.2 -263.8,-263.8 -388.4,-571.6 -388.4,-952.6 0,-256.5 44,-437.2 173.4,-684 75.7,-144.1 197.9,-280.9 747.5,-842.7 1106.5,-1133.40001 1138.2,-1165.20001 1253,-1194.50001 188.1,-51.3 214.9,-29.3 1162.7,938.00001 498.3,508.1 911.1,950.2 962.4,1030.8 263.8,415.3 283.3,964.9 48.8,1409.4 -180.8,342 -581.3,620.4 -972.2,676.6 -332.2,48.9 -671.7,-36.6 -967.3,-236.9 l -156.3,-109.9 -119.7,87.9 c -158.8,117.2 -351.8,202.7 -554.5,244.3 -183.1,39.1 -295.4,41.6 -495.7,9.8 z" |
9 | fill="#000"/> | 9 | fill="currentColor"/> |
10 | <path d="m 7991.4051,47.633899 c -39.1,-19.5 -473.9,-437.299999 -964.9,-925.800029 l -891.6,-891.59997 h -830.5 c -757.2,0 -837.8,4.9 -913.6,44 -207.6,112.4 -227.2,415.2 -39.1,561.8 66,53.7 83,53.7 950.2,53.7 989.3,0 1008.8,2.5 1094.3,173.49997 56.2,105 56.2,317.50003 4.9,427.50003 -83.1,175.9 4.8,168.5 -1915.1,168.5 h -1722 l -173.4,-63.5 c -95.3,-34.2 -232.1,-102.6 -305.3,-151.5 -73.3,-48.9 -442.1,-400.60003 -823.2,-779.2 l -688.80006,-693.7 664.40006,-647.3 c 366.4,-354.2 779.2,-754.8 918.4,-889.1 l 251.6,-241.8 481.2,481.2 481.2,481.2 h 1487.6 c 1294.6,0 1494.9,4.9 1565.8,39.1 58.6,26.9 339.6,368.8 1028.4,1248.2 522.8,666.89997 964.9,1243.3 982,1284.9 41.5,92.8 2.5,212.499999 -95.3,297.999999 -66,53.7 -95.3,61.1 -273.6,61.1 -132,-0.1 -224.8,-12.3 -273.6,-39.2 z" | 10 | <path d="m 7991.4051,47.633899 c -39.1,-19.5 -473.9,-437.299999 -964.9,-925.800029 l -891.6,-891.59997 h -830.5 c -757.2,0 -837.8,4.9 -913.6,44 -207.6,112.4 -227.2,415.2 -39.1,561.8 66,53.7 83,53.7 950.2,53.7 989.3,0 1008.8,2.5 1094.3,173.49997 56.2,105 56.2,317.50003 4.9,427.50003 -83.1,175.9 4.8,168.5 -1915.1,168.5 h -1722 l -173.4,-63.5 c -95.3,-34.2 -232.1,-102.6 -305.3,-151.5 -73.3,-48.9 -442.1,-400.60003 -823.2,-779.2 l -688.80006,-693.7 664.40006,-647.3 c 366.4,-354.2 779.2,-754.8 918.4,-889.1 l 251.6,-241.8 481.2,481.2 481.2,481.2 h 1487.6 c 1294.6,0 1494.9,4.9 1565.8,39.1 58.6,26.9 339.6,368.8 1028.4,1248.2 522.8,666.89997 964.9,1243.3 982,1284.9 41.5,92.8 2.5,212.499999 -95.3,297.999999 -66,53.7 -95.3,61.1 -273.6,61.1 -132,-0.1 -224.8,-12.3 -273.6,-39.2 z" |
11 | fill="#000"/> | 11 | fill="currentColor"/> |
12 | </g> | 12 | </g> |
13 | </g> | 13 | </g> |
14 | </svg> | 14 | </svg> |
diff --git a/client/src/assets/images/misc/video-lang.svg b/client/src/assets/images/misc/video-lang.svg index 5ffed18da..6bcaeb9be 100644 --- a/client/src/assets/images/misc/video-lang.svg +++ b/client/src/assets/images/misc/video-lang.svg | |||
@@ -1,7 +1,7 @@ | |||
1 | <svg xmlns="http://www.w3.org/2000/svg" transform="scale(1.1)" viewBox="0 0 24 24"> | 1 | <svg xmlns="http://www.w3.org/2000/svg" transform="scale(1.1)" viewBox="0 0 24 24"> |
2 | <defs/> | 2 | <defs/> |
3 | <g class="layer"> | 3 | <g class="layer"> |
4 | <path fill="#fff" fill-rule="evenodd" stroke="#000" stroke-width="1.8" d="M20.5 6.7s-.2-1.4-.8-2c-.7-.8-1.6-.8-2-.9-2.7-.2-6.9-.2-6.9-.2h0s-4.2 0-7 .2c-.3 0-1.2 0-2 .9-.5.6-.7 2-.7 2L.9 10v1.6l.2 3.3s.2 1.4.8 2c.7.8 1.7.8 2.2.9 1.6.2 6.7.2 6.7.2s4.2 0 7-.2c.3 0 1.2 0 2-.9.5-.6.7-2 .7-2l.2-3.3V10l-.2-3.3h0z"/> | 4 | <path fill="#fff" fill-rule="evenodd" stroke="currentColor" stroke-width="1.8" d="M20.5 6.7s-.2-1.4-.8-2c-.7-.8-1.6-.8-2-.9-2.7-.2-6.9-.2-6.9-.2h0s-4.2 0-7 .2c-.3 0-1.2 0-2 .9-.5.6-.7 2-.7 2L.9 10v1.6l.2 3.3s.2 1.4.8 2c.7.8 1.7.8 2.2.9 1.6.2 6.7.2 6.7.2s4.2 0 7-.2c.3 0 1.2 0 2-.9.5-.6.7-2 .7-2l.2-3.3V10l-.2-3.3h0z"/> |
5 | <path d="M8.7 14.7a.7.7 0 01-.5-1.2l2.9-3H8.7a.7.7 0 010-1.3h4a.7.7 0 01.5 1.2l-4 4a.7.7 0 01-.5.3zM11.7 8.6h-2a.7.7 0 110-1.4h2a.7.7 0 010 1.4z"/> | 5 | <path d="M8.7 14.7a.7.7 0 01-.5-1.2l2.9-3H8.7a.7.7 0 010-1.3h4a.7.7 0 01.5 1.2l-4 4a.7.7 0 01-.5.3zM11.7 8.6h-2a.7.7 0 110-1.4h2a.7.7 0 010 1.4z"/> |
6 | </g> | 6 | </g> |
7 | </svg> | 7 | </svg> |
diff --git a/client/src/assets/player/peertube-player-local-storage.ts b/client/src/assets/player/peertube-player-local-storage.ts index 75ccfe618..cf2cfb472 100644 --- a/client/src/assets/player/peertube-player-local-storage.ts +++ b/client/src/assets/player/peertube-player-local-storage.ts | |||
@@ -68,6 +68,51 @@ function getStoredLastSubtitle () { | |||
68 | return getLocalStorage('last-subtitle') | 68 | return getLocalStorage('last-subtitle') |
69 | } | 69 | } |
70 | 70 | ||
71 | function saveVideoWatchHistory(videoUUID: string, duration: number) { | ||
72 | return setLocalStorage(`video-watch-history`, JSON.stringify({ | ||
73 | ...getStoredVideoWatchHistory(), | ||
74 | [videoUUID]: { | ||
75 | duration, | ||
76 | date: `${(new Date()).toISOString()}` | ||
77 | } | ||
78 | })) | ||
79 | } | ||
80 | |||
81 | function getStoredVideoWatchHistory(videoUUID?: string) { | ||
82 | let data | ||
83 | |||
84 | try { | ||
85 | data = JSON.parse(getLocalStorage('video-watch-history')) | ||
86 | } catch (error) { | ||
87 | console.error('Cannot parse video watch history from local storage: ', error) | ||
88 | } | ||
89 | |||
90 | data = data || {} | ||
91 | |||
92 | if (videoUUID) return data[videoUUID] | ||
93 | |||
94 | return data | ||
95 | } | ||
96 | |||
97 | function cleanupVideoWatch() { | ||
98 | const data = getStoredVideoWatchHistory() | ||
99 | |||
100 | const newData = Object.keys(data).reduce((acc, videoUUID) => { | ||
101 | const date = Date.parse(data[videoUUID].date) | ||
102 | |||
103 | const diff = Math.ceil(((new Date()).getTime() - date) / (1000 * 3600 * 24)) | ||
104 | |||
105 | if (diff > 30) return acc | ||
106 | |||
107 | return { | ||
108 | ...acc, | ||
109 | [videoUUID]: data[videoUUID] | ||
110 | } | ||
111 | }, {}) | ||
112 | |||
113 | setLocalStorage('video-watch-history', JSON.stringify(newData)) | ||
114 | } | ||
115 | |||
71 | // --------------------------------------------------------------------------- | 116 | // --------------------------------------------------------------------------- |
72 | 117 | ||
73 | export { | 118 | export { |
@@ -81,7 +126,10 @@ export { | |||
81 | saveAverageBandwidth, | 126 | saveAverageBandwidth, |
82 | getAverageBandwidthInStore, | 127 | getAverageBandwidthInStore, |
83 | saveLastSubtitle, | 128 | saveLastSubtitle, |
84 | getStoredLastSubtitle | 129 | getStoredLastSubtitle, |
130 | saveVideoWatchHistory, | ||
131 | getStoredVideoWatchHistory, | ||
132 | cleanupVideoWatch | ||
85 | } | 133 | } |
86 | 134 | ||
87 | // --------------------------------------------------------------------------- | 135 | // --------------------------------------------------------------------------- |
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts index 2cbef48ea..119dec379 100644 --- a/client/src/assets/player/peertube-player-manager.ts +++ b/client/src/assets/player/peertube-player-manager.ts | |||
@@ -98,6 +98,7 @@ export interface CommonOptions extends CustomizationOptions { | |||
98 | 98 | ||
99 | videoViewUrl: string | 99 | videoViewUrl: string |
100 | embedUrl: string | 100 | embedUrl: string |
101 | embedTitle: string | ||
101 | 102 | ||
102 | isLive: boolean | 103 | isLive: boolean |
103 | 104 | ||
@@ -105,6 +106,8 @@ export interface CommonOptions extends CustomizationOptions { | |||
105 | 106 | ||
106 | videoCaptions: VideoJSCaption[] | 107 | videoCaptions: VideoJSCaption[] |
107 | 108 | ||
109 | videoUUID: string | ||
110 | |||
108 | userWatching?: UserWatching | 111 | userWatching?: UserWatching |
109 | 112 | ||
110 | serverUrl: string | 113 | serverUrl: string |
@@ -165,7 +168,7 @@ export class PeertubePlayerManager { | |||
165 | PeertubePlayerManager.alreadyPlayed = true | 168 | PeertubePlayerManager.alreadyPlayed = true |
166 | }) | 169 | }) |
167 | 170 | ||
168 | self.addContextMenu(mode, player, options.common.embedUrl) | 171 | self.addContextMenu(mode, player, options.common.embedUrl, options.common.embedTitle) |
169 | 172 | ||
170 | player.bezels() | 173 | player.bezels() |
171 | 174 | ||
@@ -203,7 +206,7 @@ export class PeertubePlayerManager { | |||
203 | videojs(newVideoElement, videojsOptions, function (this: videojs.Player) { | 206 | videojs(newVideoElement, videojsOptions, function (this: videojs.Player) { |
204 | const player = this | 207 | const player = this |
205 | 208 | ||
206 | self.addContextMenu(mode, player, options.common.embedUrl) | 209 | self.addContextMenu(mode, player, options.common.embedUrl, options.common.embedTitle) |
207 | 210 | ||
208 | PeertubePlayerManager.onPlayerChange(player) | 211 | PeertubePlayerManager.onPlayerChange(player) |
209 | }) | 212 | }) |
@@ -230,7 +233,8 @@ export class PeertubePlayerManager { | |||
230 | subtitle: commonOptions.subtitle, | 233 | subtitle: commonOptions.subtitle, |
231 | videoCaptions: commonOptions.videoCaptions, | 234 | videoCaptions: commonOptions.videoCaptions, |
232 | stopTime: commonOptions.stopTime, | 235 | stopTime: commonOptions.stopTime, |
233 | isLive: commonOptions.isLive | 236 | isLive: commonOptions.isLive, |
237 | videoUUID: commonOptions.videoUUID | ||
234 | } | 238 | } |
235 | } | 239 | } |
236 | 240 | ||
@@ -271,7 +275,7 @@ export class PeertubePlayerManager { | |||
271 | 275 | ||
272 | poster: commonOptions.poster, | 276 | poster: commonOptions.poster, |
273 | inactivityTimeout: commonOptions.inactivityTimeout, | 277 | inactivityTimeout: commonOptions.inactivityTimeout, |
274 | playbackRates: [ 0.5, 0.75, 1, 1.25, 1.5, 2 ], | 278 | playbackRates: [ 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2 ], |
275 | 279 | ||
276 | plugins, | 280 | plugins, |
277 | 281 | ||
@@ -492,7 +496,7 @@ export class PeertubePlayerManager { | |||
492 | return children | 496 | return children |
493 | } | 497 | } |
494 | 498 | ||
495 | private static addContextMenu (mode: PlayerMode, player: videojs.Player, videoEmbedUrl: string) { | 499 | private static addContextMenu (mode: PlayerMode, player: videojs.Player, videoEmbedUrl: string, videoEmbedTitle: string) { |
496 | const content = [ | 500 | const content = [ |
497 | { | 501 | { |
498 | label: player.localize('Copy the video URL'), | 502 | label: player.localize('Copy the video URL'), |
@@ -509,7 +513,7 @@ export class PeertubePlayerManager { | |||
509 | { | 513 | { |
510 | label: player.localize('Copy embed code'), | 514 | label: player.localize('Copy embed code'), |
511 | listener: () => { | 515 | listener: () => { |
512 | copyToClipboard(buildVideoOrPlaylistEmbed(videoEmbedUrl)) | 516 | copyToClipboard(buildVideoOrPlaylistEmbed(videoEmbedUrl, videoEmbedTitle)) |
513 | } | 517 | } |
514 | } | 518 | } |
515 | ] | 519 | ] |
diff --git a/client/src/assets/player/peertube-plugin.ts b/client/src/assets/player/peertube-plugin.ts index 75a6e662e..07c7e33f6 100644 --- a/client/src/assets/player/peertube-plugin.ts +++ b/client/src/assets/player/peertube-plugin.ts | |||
@@ -13,6 +13,7 @@ import { | |||
13 | getStoredVolume, | 13 | getStoredVolume, |
14 | saveLastSubtitle, | 14 | saveLastSubtitle, |
15 | saveMuteInStore, | 15 | saveMuteInStore, |
16 | saveVideoWatchHistory, | ||
16 | saveVolumeInStore | 17 | saveVolumeInStore |
17 | } from './peertube-player-local-storage' | 18 | } from './peertube-player-local-storage' |
18 | 19 | ||
@@ -120,7 +121,7 @@ class PeerTubePlugin extends Plugin { | |||
120 | this.initializePlayer() | 121 | this.initializePlayer() |
121 | this.runViewAdd() | 122 | this.runViewAdd() |
122 | 123 | ||
123 | if (options.userWatching) this.runUserWatchVideo(options.userWatching) | 124 | this.runUserWatchVideo(options.userWatching, options.videoUUID) |
124 | }) | 125 | }) |
125 | } | 126 | } |
126 | 127 | ||
@@ -178,7 +179,7 @@ class PeerTubePlugin extends Plugin { | |||
178 | }, 1000) | 179 | }, 1000) |
179 | } | 180 | } |
180 | 181 | ||
181 | private runUserWatchVideo (options: UserWatching) { | 182 | private runUserWatchVideo (options: UserWatching, videoUUID: string) { |
182 | let lastCurrentTime = 0 | 183 | let lastCurrentTime = 0 |
183 | 184 | ||
184 | this.userWatchingVideoInterval = setInterval(() => { | 185 | this.userWatchingVideoInterval = setInterval(() => { |
@@ -187,8 +188,12 @@ class PeerTubePlugin extends Plugin { | |||
187 | if (currentTime - lastCurrentTime >= 1) { | 188 | if (currentTime - lastCurrentTime >= 1) { |
188 | lastCurrentTime = currentTime | 189 | lastCurrentTime = currentTime |
189 | 190 | ||
190 | this.notifyUserIsWatching(currentTime, options.url, options.authorizationHeader) | 191 | if (options) { |
191 | .catch(err => console.error('Cannot notify user is watching.', err)) | 192 | this.notifyUserIsWatching(currentTime, options.url, options.authorizationHeader) |
193 | .catch(err => console.error('Cannot notify user is watching.', err)) | ||
194 | } else { | ||
195 | saveVideoWatchHistory(videoUUID, currentTime) | ||
196 | } | ||
192 | } | 197 | } |
193 | }, this.CONSTANTS.USER_WATCHING_VIDEO_INTERVAL) | 198 | }, this.CONSTANTS.USER_WATCHING_VIDEO_INTERVAL) |
194 | } | 199 | } |
diff --git a/client/src/assets/player/peertube-videojs-typings.ts b/client/src/assets/player/peertube-videojs-typings.ts index e5259092c..4a6c80247 100644 --- a/client/src/assets/player/peertube-videojs-typings.ts +++ b/client/src/assets/player/peertube-videojs-typings.ts | |||
@@ -108,6 +108,8 @@ type PeerTubePluginOptions = { | |||
108 | stopTime: number | string | 108 | stopTime: number | string |
109 | 109 | ||
110 | isLive: boolean | 110 | isLive: boolean |
111 | |||
112 | videoUUID: string | ||
111 | } | 113 | } |
112 | 114 | ||
113 | type PlaylistPluginOptions = { | 115 | type PlaylistPluginOptions = { |
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts index 6767459ce..d7451fa1d 100644 --- a/client/src/assets/player/utils.ts +++ b/client/src/assets/player/utils.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { VideoFile } from '@shared/models' | 1 | import { VideoFile } from '@shared/models' |
2 | import { escapeHTML } from '@shared/core-utils/renderer' | ||
2 | 3 | ||
3 | function toTitleCase (str: string) { | 4 | function toTitleCase (str: string) { |
4 | return str.charAt(0).toUpperCase() + str.slice(1) | 5 | return str.charAt(0).toUpperCase() + str.slice(1) |
@@ -170,9 +171,11 @@ function secondsToTime (seconds: number, full = false, symbol?: string) { | |||
170 | return time | 171 | return time |
171 | } | 172 | } |
172 | 173 | ||
173 | function buildVideoOrPlaylistEmbed (embedUrl: string) { | 174 | function buildVideoOrPlaylistEmbed (embedUrl: string, embedTitle: string) { |
175 | const title = escapeHTML(embedTitle) | ||
174 | return '<iframe width="560" height="315" ' + | 176 | return '<iframe width="560" height="315" ' + |
175 | 'sandbox="allow-same-origin allow-scripts allow-popups" ' + | 177 | 'sandbox="allow-same-origin allow-scripts allow-popups" ' + |
178 | 'title="' + title + '" ' + | ||
176 | 'src="' + embedUrl + '" ' + | 179 | 'src="' + embedUrl + '" ' + |
177 | 'frameborder="0" allowfullscreen>' + | 180 | 'frameborder="0" allowfullscreen>' + |
178 | '</iframe>' | 181 | '</iframe>' |
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index a0009eecc..c01938147 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss | |||
@@ -15,6 +15,8 @@ $assets-path: '../../assets/'; | |||
15 | @import './primeng-custom'; | 15 | @import './primeng-custom'; |
16 | @import './ng-select.scss'; | 16 | @import './ng-select.scss'; |
17 | 17 | ||
18 | @import './classes.scss'; | ||
19 | |||
18 | [hidden] { | 20 | [hidden] { |
19 | display: none !important; | 21 | display: none !important; |
20 | } | 22 | } |
@@ -36,7 +38,9 @@ body { | |||
36 | 38 | ||
37 | --menuBackgroundColor: #{$menu-background}; | 39 | --menuBackgroundColor: #{$menu-background}; |
38 | --menuForegroundColor: #{$menu-color}; | 40 | --menuForegroundColor: #{$menu-color}; |
39 | --submenuColor: #{$sub-menu-color}; | 41 | |
42 | --submenuBackgroundColor: #{$sub-menu-background-color}; | ||
43 | --channelBackgroundColor: #{$channel-background-color}; | ||
40 | 44 | ||
41 | --inputForegroundColor: #{$input-foreground-color}; | 45 | --inputForegroundColor: #{$input-foreground-color}; |
42 | --inputBackgroundColor: #{$input-background-color}; | 46 | --inputBackgroundColor: #{$input-background-color}; |
@@ -53,7 +57,9 @@ body { | |||
53 | 57 | ||
54 | --activatedActionButtonColor: #{$activated-action-button-color}; | 58 | --activatedActionButtonColor: #{$activated-action-button-color}; |
55 | 59 | ||
56 | --expanded-horizontal-margin-content: #{$expanded-horizontal-margins}; | 60 | --horizontalMarginContent: #{$not-expanded-horizontal-margins}; |
61 | --videosHorizontalMarginContent: 6vw; | ||
62 | --mainColWidth: calc(100vw - #{$menu-width}); | ||
57 | 63 | ||
58 | font-family: $main-fonts; | 64 | font-family: $main-fonts; |
59 | font-weight: $font-regular; | 65 | font-weight: $font-regular; |
@@ -146,24 +152,26 @@ my-input-toggle-hidden ::ng-deep input { | |||
146 | outline: none; | 152 | outline: none; |
147 | 153 | ||
148 | .margin-content { | 154 | .margin-content { |
149 | margin-left: $not-expanded-horizontal-margins; | 155 | margin-left: pvar(--horizontalMarginContent); |
150 | margin-right: $not-expanded-horizontal-margins; | 156 | margin-right: pvar(--horizontalMarginContent); |
151 | flex-grow: 1; | 157 | flex-grow: 1; |
152 | } | 158 | } |
153 | 159 | ||
154 | .sub-menu { | 160 | .sub-menu { |
155 | background-color: pvar(--submenuColor); | 161 | background-color: pvar(--submenuBackgroundColor); |
156 | width: 100%; | 162 | width: 100%; |
157 | display: flex; | 163 | display: flex; |
158 | align-items: center; | 164 | align-items: center; |
159 | padding-left: $not-expanded-horizontal-margins; | 165 | padding-left: pvar(--horizontalMarginContent); |
160 | padding-right: $not-expanded-horizontal-margins; | 166 | padding-right: pvar(--horizontalMarginContent); |
161 | height: $sub-menu-height; | 167 | height: $sub-menu-height; |
162 | margin-bottom: $sub-menu-margin-bottom; | 168 | margin-bottom: $sub-menu-margin-bottom; |
169 | overflow-x: auto; | ||
163 | 170 | ||
164 | &.sub-menu-fixed { | 171 | &.sub-menu-fixed { |
165 | position: fixed; | 172 | position: fixed; |
166 | z-index: #{z('sub-menu') - 1}; | 173 | z-index: #{z('sub-menu') - 1}; |
174 | max-width: pvar(--mainColWidth); | ||
167 | } | 175 | } |
168 | } | 176 | } |
169 | 177 | ||
@@ -174,18 +182,11 @@ my-input-toggle-hidden ::ng-deep input { | |||
174 | 182 | ||
175 | // Override some properties if the main content is expanded (no menu on the left) | 183 | // Override some properties if the main content is expanded (no menu on the left) |
176 | &.expanded { | 184 | &.expanded { |
185 | --horizontalMarginContent: #{$expanded-horizontal-margins}; | ||
186 | --mainColWidth: 100vw; | ||
187 | |||
177 | margin-left: 0; | 188 | margin-left: 0; |
178 | width: 100%; | 189 | width: 100%; |
179 | |||
180 | .margin-content { | ||
181 | margin-left: var(--expanded-horizontal-margin-content); | ||
182 | margin-right: var(--expanded-horizontal-margin-content); | ||
183 | } | ||
184 | |||
185 | .sub-menu { | ||
186 | padding-left: var(--expanded-horizontal-margin-content); | ||
187 | padding-right: var(--expanded-horizontal-margin-content); | ||
188 | } | ||
189 | } | 190 | } |
190 | 191 | ||
191 | &.lock-scroll .main-row > router-outlet + * { | 192 | &.lock-scroll .main-row > router-outlet + * { |
@@ -263,7 +264,7 @@ my-input-toggle-hidden ::ng-deep input { | |||
263 | opacity: 0.6; | 264 | opacity: 0.6; |
264 | 265 | ||
265 | &.active { | 266 | &.active { |
266 | background-color: pvar(--submenuColor); | 267 | background-color: pvar(--submenuBackgroundColor); |
267 | } | 268 | } |
268 | 269 | ||
269 | &.active, &:hover, &:active, &:focus { | 270 | &.active, &:hover, &:active, &:focus { |
@@ -277,11 +278,6 @@ my-input-toggle-hidden ::ng-deep input { | |||
277 | font-weight: bold; | 278 | font-weight: bold; |
278 | } | 279 | } |
279 | 280 | ||
280 | @keyframes spin { | ||
281 | from { transform: scale(1) rotate(0deg);} | ||
282 | to { transform: scale(1) rotate(360deg);} | ||
283 | } | ||
284 | |||
285 | // In tables, don't have a hover different background | 281 | // In tables, don't have a hover different background |
286 | table { | 282 | table { |
287 | .action-button-edit, .action-button-delete { | 283 | .action-button-edit, .action-button-delete { |
@@ -338,29 +334,34 @@ ngx-loading-bar { | |||
338 | 334 | ||
339 | @media screen and (max-width: #{breakpoint(xxl)}) { | 335 | @media screen and (max-width: #{breakpoint(xxl)}) { |
340 | .main-col { | 336 | .main-col { |
337 | & { | ||
338 | --horizontalMarginContent: #{$not-expanded-horizontal-margins / 2}; | ||
339 | } | ||
340 | |||
341 | &.expanded { | 341 | &.expanded { |
342 | .margin-content { | 342 | --horizontalMarginContent: #{$expanded-horizontal-margins / 2}; |
343 | --expanded-horizontal-margin-content: #{$expanded-horizontal-margins/2}; | ||
344 | } | ||
345 | } | 343 | } |
344 | |||
345 | --videosHorizontalMarginContent: 30px; | ||
346 | } | 346 | } |
347 | } | 347 | } |
348 | 348 | ||
349 | @media screen and (max-width: #{breakpoint(lg)}) { | 349 | @media screen and (max-width: #{breakpoint(lg)}) { |
350 | /* the following applies from 500px to 900px and is partially overriden from 500px to 800px by changes below to $small-view */ | ||
351 | .main-col { | 350 | .main-col { |
352 | &, &.expanded { | 351 | --videosHorizontalMarginContent: #{pvar(--horizontalMarginContent)}; |
353 | .margin-content { | 352 | } |
354 | --expanded-horizontal-margin-content: #{$expanded-horizontal-margins/3}; | 353 | |
355 | } | 354 | /* the following applies from 500px to 900px and is partially overriden from 500px to 800px by changes below to $small-view */ |
355 | .main-col, | ||
356 | .main-col.expanded { | ||
357 | --horizontalMarginContent: #{$expanded-horizontal-margins / 3}; | ||
356 | 358 | ||
357 | .sub-menu { | 359 | .sub-menu { |
358 | padding-left: 50px; | 360 | padding-left: 50px; |
359 | padding-right: 50px; | 361 | padding-right: 50px; |
360 | 362 | ||
361 | .title-page { | 363 | .title-page { |
362 | font-size: 17px; | 364 | font-size: 17px; |
363 | } | ||
364 | } | 365 | } |
365 | } | 366 | } |
366 | } | 367 | } |
@@ -373,98 +374,46 @@ ngx-loading-bar { | |||
373 | } | 374 | } |
374 | 375 | ||
375 | @media screen and (max-width: $small-view) { | 376 | @media screen and (max-width: $small-view) { |
376 | .main-col { | 377 | .main-col, |
377 | margin-left: 0; | 378 | .main-col.expanded { |
378 | 379 | --horizontalMarginContent: 15px; | |
379 | &, &.expanded { | ||
380 | .margin-content { | ||
381 | --expanded-horizontal-margin-content: 15px; | ||
382 | } | ||
383 | |||
384 | .sub-menu { | ||
385 | width: 100vw; | ||
386 | padding-left: 15px; | ||
387 | padding-right: 15px; | ||
388 | margin-bottom: $sub-menu-margin-bottom-small-view; | ||
389 | overflow-x: auto; | ||
390 | } | ||
391 | |||
392 | // Use an appropriate offset top when sub-menu fixed | ||
393 | .margin-content.offset-content { | ||
394 | padding-top: $sub-menu-height + $sub-menu-margin-bottom-small-view; | ||
395 | } | ||
396 | |||
397 | .admin-sub-header { | ||
398 | @include admin-sub-header-responsive(15px*2); | ||
399 | } | ||
400 | 380 | ||
401 | my-markdown-textarea { | 381 | margin-left: 0; |
402 | .root { | ||
403 | max-width: 100% !important; | ||
404 | } | ||
405 | } | ||
406 | |||
407 | input[type=text], | ||
408 | input[type=password], | ||
409 | input[type=email], | ||
410 | textarea, | ||
411 | .peertube-select-container { | ||
412 | flex-grow: 1; | ||
413 | } | ||
414 | 382 | ||
415 | .caption input[type=text] { | 383 | .sub-menu { |
416 | width: unset !important; | 384 | width: 100vw; |
417 | flex-grow: 1; | 385 | padding-left: 15px; |
418 | } | 386 | padding-right: 15px; |
387 | margin-bottom: $sub-menu-margin-bottom-small-view; | ||
388 | overflow-x: auto; | ||
419 | } | 389 | } |
420 | } | ||
421 | } | ||
422 | 390 | ||
423 | // overflow-databale responsive rules | 391 | // Use an appropriate offset top when sub-menu fixed |
424 | @media screen and (min-width: #{breakpoint(lg)}) { | 392 | .margin-content.offset-content { |
425 | .main-col { | 393 | padding-top: $sub-menu-height + $sub-menu-margin-bottom-small-view; |
426 | &.expanded { | ||
427 | @include overflow-datatable(breakpoint(lg), $expanded-horizontal-margins/2, $mobile-paginator: false); | ||
428 | } | 394 | } |
429 | 395 | ||
430 | &:not(.expanded) { | 396 | .admin-sub-header { |
431 | @include overflow-datatable(breakpoint(lg), $not-expanded-horizontal-margins + $menu-width/2, $mobile-paginator: false); | 397 | @include admin-sub-header-responsive; |
432 | } | 398 | } |
433 | } | ||
434 | } | ||
435 | 399 | ||
436 | @media screen and (max-width: #{breakpoint(lg)}) { | 400 | my-markdown-textarea { |
437 | .main-col { | 401 | .root { |
438 | &.expanded { | 402 | max-width: 100% !important; |
439 | @include overflow-datatable(breakpoint(lg), $expanded-horizontal-margins/3); | 403 | } |
440 | } | 404 | } |
441 | 405 | ||
442 | &:not(.expanded) { | 406 | input[type=text], |
443 | @include overflow-datatable(breakpoint(lg), $expanded-horizontal-margins/3 + $menu-width/2); | 407 | input[type=password], |
444 | } | 408 | input[type=email], |
445 | } | 409 | textarea, |
446 | } | 410 | .peertube-select-container { |
447 | 411 | flex-grow: 1; | |
448 | @media screen and (max-width: $small-view) { | ||
449 | .main-col { | ||
450 | &:not(.expanded), | ||
451 | &.expanded { | ||
452 | @include overflow-datatable(breakpoint(lg), 15px); | ||
453 | } | 412 | } |
454 | } | ||
455 | } | ||
456 | 413 | ||
457 | @media screen and (min-width: $small-view) and (max-width: #{$small-view + $menu-width}) { | 414 | .caption input[type=text] { |
458 | .main-col { | 415 | width: unset !important; |
459 | &:not(.expanded) { | 416 | flex-grow: 1; |
460 | .admin-sub-header { | ||
461 | @include admin-sub-header-responsive($expanded-horizontal-margins/3 + $menu-width/2); | ||
462 | } | ||
463 | |||
464 | .sub-menu { | ||
465 | overflow-x: auto; | ||
466 | width: calc(100vw - #{$menu-width}); | ||
467 | } | ||
468 | } | 417 | } |
469 | } | 418 | } |
470 | } | 419 | } |
diff --git a/client/src/sass/bootstrap.scss b/client/src/sass/bootstrap.scss index 7047f6e03..75dc91d7a 100644 --- a/client/src/sass/bootstrap.scss +++ b/client/src/sass/bootstrap.scss | |||
@@ -9,6 +9,10 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/'; | |||
9 | animation: spin .7s infinite linear; | 9 | animation: spin .7s infinite linear; |
10 | } | 10 | } |
11 | 11 | ||
12 | .glyphicon-duplicate { | ||
13 | font-size: 70%; | ||
14 | } | ||
15 | |||
12 | .flex-auto { | 16 | .flex-auto { |
13 | flex: auto; | 17 | flex: auto; |
14 | } | 18 | } |
diff --git a/client/src/sass/classes.scss b/client/src/sass/classes.scss new file mode 100644 index 000000000..af8e39573 --- /dev/null +++ b/client/src/sass/classes.scss | |||
@@ -0,0 +1,22 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .peertube-button { | ||
5 | @include peertube-button; | ||
6 | } | ||
7 | |||
8 | .peertube-button-link { | ||
9 | @include peertube-button-link; | ||
10 | } | ||
11 | |||
12 | .orange-button { | ||
13 | @include orange-button; | ||
14 | } | ||
15 | |||
16 | .orange-button-inverted { | ||
17 | @include orange-button-inverted; | ||
18 | } | ||
19 | |||
20 | .grey-button { | ||
21 | @include grey-button; | ||
22 | } | ||
diff --git a/client/src/sass/include/_actor.scss b/client/src/sass/include/_actor.scss new file mode 100644 index 000000000..8d82a042c --- /dev/null +++ b/client/src/sass/include/_actor.scss | |||
@@ -0,0 +1,92 @@ | |||
1 | @import '_variables'; | ||
2 | |||
3 | @mixin section-label-responsive { | ||
4 | color: pvar(--mainColor); | ||
5 | font-size: 12px; | ||
6 | margin-bottom: 15px; | ||
7 | font-weight: $font-bold; | ||
8 | letter-spacing: 2.5px; | ||
9 | |||
10 | @media screen and (max-width: $mobile-view) { | ||
11 | font-size: 10px; | ||
12 | letter-spacing: 2.1px; | ||
13 | margin-bottom: 5px; | ||
14 | } | ||
15 | } | ||
16 | |||
17 | @mixin show-more-description { | ||
18 | color: pvar(--mainColor); | ||
19 | cursor: pointer; | ||
20 | margin: 10px auto 45px auto; | ||
21 | } | ||
22 | |||
23 | @mixin avatar-row-responsive ($img-margin, $grey-font-size) { | ||
24 | display: flex; | ||
25 | grid-column: 1; | ||
26 | margin-bottom: 30px; | ||
27 | |||
28 | .channel-avatar { | ||
29 | @include channel-avatar(120px); | ||
30 | } | ||
31 | |||
32 | .account-avatar { | ||
33 | @include avatar(120px); | ||
34 | } | ||
35 | |||
36 | > div { | ||
37 | margin-left: $img-margin; | ||
38 | min-width: 1px; | ||
39 | } | ||
40 | |||
41 | .actor-info { | ||
42 | display: flex; | ||
43 | |||
44 | > div:first-child { | ||
45 | flex-grow: 1; | ||
46 | min-width: 1px; | ||
47 | } | ||
48 | } | ||
49 | |||
50 | .actor-display-name { | ||
51 | display: flex; | ||
52 | flex-wrap: wrap; | ||
53 | } | ||
54 | |||
55 | h1 { | ||
56 | font-size: 28px; | ||
57 | font-weight: $font-bold; | ||
58 | margin: 0; | ||
59 | } | ||
60 | |||
61 | .actor-handle { | ||
62 | @include ellipsis; | ||
63 | } | ||
64 | |||
65 | .actor-handle, | ||
66 | .actor-counters { | ||
67 | color: pvar(--greyForegroundColor); | ||
68 | font-size: $grey-font-size; | ||
69 | } | ||
70 | |||
71 | .actor-counters > *:not(:last-child)::after { | ||
72 | content: '•'; | ||
73 | margin: 0 10px; | ||
74 | color: pvar(--mainColor); | ||
75 | } | ||
76 | |||
77 | @media screen and (max-width: $mobile-view) { | ||
78 | margin-bottom: 15px; | ||
79 | |||
80 | h1 { | ||
81 | font-size: 22px; | ||
82 | } | ||
83 | |||
84 | .channel-avatar { | ||
85 | @include channel-avatar(80px); | ||
86 | } | ||
87 | |||
88 | .account-avatar { | ||
89 | @include avatar(120px); | ||
90 | } | ||
91 | } | ||
92 | } | ||
diff --git a/client/src/sass/include/_miniature.scss b/client/src/sass/include/_miniature.scss index 134b307b1..3b86f29b4 100644 --- a/client/src/sass/include/_miniature.scss +++ b/client/src/sass/include/_miniature.scss | |||
@@ -4,11 +4,11 @@ | |||
4 | @mixin miniature-name { | 4 | @mixin miniature-name { |
5 | @include ellipsis-multiline(1.1em, 2); | 5 | @include ellipsis-multiline(1.1em, 2); |
6 | 6 | ||
7 | word-break: break-all; | ||
8 | word-wrap: break-word; | ||
7 | transition: color 0.2s; | 9 | transition: color 0.2s; |
8 | font-weight: $font-semibold; | 10 | font-weight: $font-semibold; |
9 | color: pvar(--mainForegroundColor); | 11 | color: pvar(--mainForegroundColor); |
10 | margin-top: 10px; | ||
11 | margin-bottom: 5px; | ||
12 | 12 | ||
13 | &:hover { | 13 | &:hover { |
14 | text-decoration: none; | 14 | text-decoration: none; |
@@ -20,20 +20,20 @@ | |||
20 | } | 20 | } |
21 | } | 21 | } |
22 | 22 | ||
23 | $play-overlay-transition: 0.2s ease; | ||
24 | $play-overlay-height: 26px; | ||
25 | $play-overlay-width: 18px; | ||
26 | |||
27 | @mixin miniature-thumbnail { | 23 | @mixin miniature-thumbnail { |
28 | @include disable-outline; | 24 | @include disable-outline; |
29 | 25 | ||
26 | $play-overlay-transition: 0.2s ease; | ||
27 | $play-overlay-height: 26px; | ||
28 | $play-overlay-width: 18px; | ||
29 | |||
30 | display: flex; | 30 | display: flex; |
31 | flex-direction: column; | 31 | flex-direction: column; |
32 | position: relative; | 32 | position: relative; |
33 | border-radius: 3px; | 33 | border-radius: 3px; |
34 | width: 100%; | ||
35 | height: 100%; | ||
34 | overflow: hidden; | 36 | overflow: hidden; |
35 | width: $video-thumbnail-width; | ||
36 | height: $video-thumbnail-height; | ||
37 | background-color: #ececec; | 37 | background-color: #ececec; |
38 | transition: filter $play-overlay-transition; | 38 | transition: filter $play-overlay-transition; |
39 | 39 | ||
@@ -97,154 +97,64 @@ $play-overlay-width: 18px; | |||
97 | color: #fff; | 97 | color: #fff; |
98 | } | 98 | } |
99 | 99 | ||
100 | @mixin miniature-rows { | 100 | // Use margin by default, or padding if $margin is false |
101 | &:first-child { | 101 | @mixin grid-videos-miniature-margins ($margin: true, $min-margin: 0) { |
102 | padding-top: 30px; | 102 | --gridVideosMiniatureMargins: #{pvar(--videosHorizontalMarginContent)}; |
103 | 103 | ||
104 | .section-title { | 104 | @if $margin { |
105 | border-top: none !important; | 105 | margin-left: var(--gridVideosMiniatureMargins) !important; |
106 | } | 106 | margin-right: var(--gridVideosMiniatureMargins) !important; |
107 | } | 107 | } @else { |
108 | 108 | padding-left: var(--gridVideosMiniatureMargins) !important; | |
109 | .section-title { | 109 | padding-right: var(--gridVideosMiniatureMargins) !important; |
110 | font-size: 24px; | ||
111 | font-weight: $font-semibold; | ||
112 | padding-top: 15px; | ||
113 | margin-bottom: 15px; | ||
114 | display: flex; | ||
115 | justify-content: space-between; | ||
116 | |||
117 | &:not(h2) { | ||
118 | border-top: 1px solid $separator-border-color; | ||
119 | } | ||
120 | |||
121 | a { | ||
122 | &:hover, &:focus:not(.focus-visible), &:active { | ||
123 | text-decoration: none; | ||
124 | outline: none; | ||
125 | } | ||
126 | |||
127 | color: pvar(--mainForegroundColor); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | &.channel { | ||
132 | .section-title { | ||
133 | a { | ||
134 | display: flex; | ||
135 | width: fit-content; | ||
136 | align-items: center; | ||
137 | |||
138 | img { | ||
139 | @include avatar(28px); | ||
140 | |||
141 | margin-right: 8px; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | .followers { | ||
146 | color: pvar(--greyForegroundColor); | ||
147 | font-weight: normal; | ||
148 | font-size: 14px; | ||
149 | margin-left: 10px; | ||
150 | position: relative; | ||
151 | top: 2px; | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | .show-more { | ||
157 | position: relative; | ||
158 | top: -5px; | ||
159 | display: inline-block; | ||
160 | font-size: 16px; | ||
161 | text-transform: uppercase; | ||
162 | color: pvar(--greyForegroundColor); | ||
163 | margin-bottom: 10px; | ||
164 | font-weight: $font-semibold; | ||
165 | text-decoration: none; | ||
166 | } | 110 | } |
167 | 111 | ||
168 | @media screen and (max-width: $mobile-view) { | 112 | @media screen and (max-width: $mobile-view) { |
169 | max-height: initial; | 113 | --gridVideosMiniatureMargins: #{$min-margin}; |
170 | overflow: initial; | ||
171 | |||
172 | .section-title { | ||
173 | font-size: 17px; | ||
174 | margin-left: 10px; | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | 114 | ||
179 | @mixin fluid-videos-miniature-layout { | ||
180 | margin-left: $not-expanded-horizontal-margins !important; | ||
181 | margin-right: $not-expanded-horizontal-margins !important; | ||
182 | |||
183 | @media screen and (max-width: $mobile-view) { | ||
184 | width: auto; | 115 | width: auto; |
185 | margin: 0 !important; | ||
186 | |||
187 | .videos { | ||
188 | text-align: center; | ||
189 | |||
190 | ::ng-deep .video-miniature { | ||
191 | padding-right: 0; | ||
192 | height: auto; | ||
193 | width: 100%; | ||
194 | margin-bottom: 25px; | ||
195 | |||
196 | .video-miniature-information { | ||
197 | width: 100% !important; | ||
198 | text-align: left; | ||
199 | |||
200 | span { | ||
201 | width: 100%; | ||
202 | } | ||
203 | } | ||
204 | |||
205 | .video-thumbnail { | ||
206 | border-radius: 0; | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | } | 116 | } |
117 | } | ||
211 | 118 | ||
212 | @media screen and (min-width: #{breakpoint(fhd)}) { | 119 | @mixin grid-videos-miniature-layout { |
213 | margin-left: 6vw !important; | 120 | @include grid-videos-miniature-margins; |
214 | margin-right: 6vw !important; | ||
215 | } | ||
216 | 121 | ||
217 | @media screen and (min-width: $mobile-view) { | 122 | @media screen and (min-width: $mobile-view) { |
218 | 123 | .videos, | |
219 | .videos { | 124 | .playlists { |
220 | --miniature-min-width: #{$video-thumbnail-width - 15px}; | 125 | --miniatureMinWidth: #{$video-thumbnail-width - 25px}; |
221 | --miniature-max-width: #{$video-thumbnail-width}; | 126 | --miniatureMaxWidth: #{$video-thumbnail-width}; |
222 | 127 | ||
223 | display: grid; | 128 | display: grid; |
224 | column-gap: 5px; | 129 | column-gap: 30px; |
225 | grid-template-columns: repeat( | 130 | grid-template-columns: repeat( |
226 | auto-fill, | 131 | auto-fill, |
227 | minmax( | 132 | minmax( |
228 | var(--miniature-min-width), | 133 | var(--miniatureMinWidth), |
229 | 1fr | 134 | 1fr |
230 | ) | 135 | ) |
231 | ); | 136 | ); |
232 | 137 | ||
233 | @media screen and (min-width: #{breakpoint(fhd)}) { | 138 | .video-wrapper, |
234 | column-gap: 1%; | 139 | .playlist-wrapper { |
235 | --miniature-min-width: #{$video-thumbnail-width}; | ||
236 | } | ||
237 | |||
238 | .video-wrapper { | ||
239 | margin: 0 auto; | 140 | margin: 0 auto; |
240 | width: 100%; | 141 | width: 100%; |
241 | 142 | ||
242 | my-video-miniature { | 143 | my-video-miniature, |
144 | my-video-playlist-miniature { | ||
243 | display: block; | 145 | display: block; |
244 | min-width: var(--miniature-min-width); | 146 | min-width: var(--miniatureMinWidth); |
245 | max-width: var(--miniature-max-width); | 147 | max-width: var(--miniatureMaxWidth); |
246 | } | 148 | } |
247 | } | 149 | } |
150 | |||
151 | @media screen and (min-width: #{breakpoint(xm)}) { | ||
152 | column-gap: 15px; | ||
153 | } | ||
154 | |||
155 | @media screen and (min-width: #{breakpoint(fhd)}) { | ||
156 | column-gap: 2%; | ||
157 | } | ||
248 | } | 158 | } |
249 | } | 159 | } |
250 | } | 160 | } |
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss index ca11488cb..bf844ac5d 100644 --- a/client/src/sass/include/_mixins.scss +++ b/client/src/sass/include/_mixins.scss | |||
@@ -23,17 +23,28 @@ | |||
23 | display: block; | 23 | display: block; |
24 | /* Fallback for non-webkit */ | 24 | /* Fallback for non-webkit */ |
25 | display: -webkit-box; | 25 | display: -webkit-box; |
26 | max-height: $font-size * $number-of-lines; | 26 | -webkit-line-clamp: $number-of-lines; |
27 | /* Fallback for non-webkit */ | 27 | /* Fallback for non-webkit */ |
28 | font-size: $font-size; | 28 | font-size: $font-size; |
29 | line-height: $font-size; | 29 | line-height: $font-size; |
30 | overflow: hidden; | 30 | overflow: hidden; |
31 | text-overflow: ellipsis; | 31 | text-overflow: ellipsis; |
32 | max-height: $font-size * $number-of-lines; | ||
32 | } | 33 | } |
33 | 34 | ||
34 | @mixin prefix($property, $parameters...) { | 35 | @mixin fade-text ($fade-after, $background-color) { |
35 | @each $prefix in -webkit-, -moz-, -ms-, -o-, "" { | 36 | position: relative; |
36 | #{$prefix}#{$property}: $parameters; | 37 | overflow: hidden; |
38 | |||
39 | &:after { | ||
40 | content: ''; | ||
41 | pointer-events: none; | ||
42 | width: 100%; | ||
43 | height: 100%; | ||
44 | position: absolute; | ||
45 | left: 0; | ||
46 | top: 0; | ||
47 | background: linear-gradient(transparent $fade-after, $background-color); | ||
37 | } | 48 | } |
38 | } | 49 | } |
39 | 50 | ||
@@ -41,9 +52,6 @@ | |||
41 | word-break: break-word; | 52 | word-break: break-word; |
42 | word-wrap: break-word; | 53 | word-wrap: break-word; |
43 | overflow-wrap: break-word; | 54 | overflow-wrap: break-word; |
44 | -webkit-hyphens: auto; | ||
45 | -ms-hyphens: auto; | ||
46 | -moz-hyphens: auto; | ||
47 | hyphens: auto; | 55 | hyphens: auto; |
48 | } | 56 | } |
49 | 57 | ||
@@ -52,28 +60,6 @@ | |||
52 | ::ng-deep .material { | 60 | ::ng-deep .material { |
53 | color: $color; | 61 | color: $color; |
54 | } | 62 | } |
55 | |||
56 | ::ng-deep svg { | ||
57 | path[fill="#000"], | ||
58 | g[fill="#000"], | ||
59 | rect[fill="#000"], | ||
60 | circle[fill="#000"], | ||
61 | polygon[fill="#000"] { | ||
62 | fill: $color; | ||
63 | } | ||
64 | |||
65 | path[stroke="#000"], | ||
66 | g[stroke="#000"], | ||
67 | rect[stroke="#000"], | ||
68 | circle[stroke="#000"], | ||
69 | polygon[stroke="#000"] { | ||
70 | stroke: $color; | ||
71 | } | ||
72 | |||
73 | stop[stop-color="#000"] { | ||
74 | stop-color: $color; | ||
75 | } | ||
76 | } | ||
77 | } | 63 | } |
78 | 64 | ||
79 | @mixin fill-svg-color ($color) { | 65 | @mixin fill-svg-color ($color) { |
@@ -163,6 +149,33 @@ | |||
163 | } | 149 | } |
164 | } | 150 | } |
165 | 151 | ||
152 | @mixin orange-button-inverted { | ||
153 | @include button-focus(pvar(--mainColorLightest)); | ||
154 | |||
155 | border: 2px solid pvar(--mainColor); | ||
156 | font-weight: $font-semibold; | ||
157 | |||
158 | &, &:active, &:focus { | ||
159 | color: pvar(--mainColor); | ||
160 | background-color: pvar(--mainBackgroundColor); | ||
161 | } | ||
162 | |||
163 | &:hover { | ||
164 | color: pvar(--mainColor); | ||
165 | background-color: pvar(--mainColorLightest); | ||
166 | } | ||
167 | |||
168 | &[disabled], &.disabled { | ||
169 | cursor: default; | ||
170 | color: pvar(--mainColor); | ||
171 | background-color: #C6C6C6; | ||
172 | } | ||
173 | |||
174 | my-global-icon { | ||
175 | @include apply-svg-color(pvar(--mainColor)) | ||
176 | } | ||
177 | } | ||
178 | |||
166 | @mixin tertiary-button { | 179 | @mixin tertiary-button { |
167 | @include button-focus($grey-button-outline-color); | 180 | @include button-focus($grey-button-outline-color); |
168 | 181 | ||
@@ -534,6 +547,14 @@ | |||
534 | min-height: $size; | 547 | min-height: $size; |
535 | } | 548 | } |
536 | 549 | ||
550 | @mixin channel-avatar ($size) { | ||
551 | width: $size; | ||
552 | height: $size; | ||
553 | min-width: $size; | ||
554 | min-height: $size; | ||
555 | border-radius: 5px; | ||
556 | } | ||
557 | |||
537 | @mixin chevron ($size, $border-width) { | 558 | @mixin chevron ($size, $border-width) { |
538 | border-style: solid; | 559 | border-style: solid; |
539 | border-width: $border-width $border-width 0 0; | 560 | border-width: $border-width $border-width 0 0; |
@@ -593,103 +614,29 @@ | |||
593 | } | 614 | } |
594 | } | 615 | } |
595 | 616 | ||
596 | @mixin sub-menu-with-actor { | ||
597 | position: initial; | ||
598 | z-index: unset; | ||
599 | height: max-content; | ||
600 | display: flex; | ||
601 | flex-direction: column; | ||
602 | align-items: flex-start; | ||
603 | |||
604 | .actor { | ||
605 | display: flex; | ||
606 | margin-top: 20px; | ||
607 | margin-bottom: 20px; | ||
608 | |||
609 | img { | ||
610 | @include avatar(80px); | ||
611 | |||
612 | margin-right: 20px; | ||
613 | } | ||
614 | |||
615 | .actor-info { | ||
616 | display: flex; | ||
617 | flex-direction: column; | ||
618 | justify-content: center; | ||
619 | |||
620 | .actor-names { | ||
621 | display: flex; | ||
622 | align-items: center; | ||
623 | flex-wrap: wrap; | ||
624 | |||
625 | .actor-display-name { | ||
626 | font-size: 23px; | ||
627 | font-weight: $font-bold; | ||
628 | margin-right: 7px; | ||
629 | } | ||
630 | |||
631 | .actor-name { | ||
632 | position: relative; | ||
633 | top: 3px; | ||
634 | font-size: 14px; | ||
635 | color: $grey-actor-name; | ||
636 | } | ||
637 | } | ||
638 | |||
639 | .actor-lower { | ||
640 | grid-area: lower; | ||
641 | } | ||
642 | |||
643 | .actor-followers { | ||
644 | font-size: 15px; | ||
645 | } | ||
646 | |||
647 | .actor-owner { | ||
648 | @include actor-owner; | ||
649 | } | ||
650 | } | ||
651 | } | ||
652 | |||
653 | .links { | ||
654 | margin-top: 0; | ||
655 | margin-bottom: 15px; | ||
656 | |||
657 | a { | ||
658 | margin-top: 0; | ||
659 | margin-bottom: 0; | ||
660 | text-transform: uppercase; | ||
661 | font-weight: 600; | ||
662 | font-size: 110%; | ||
663 | |||
664 | @media screen and (max-width: $mobile-view) { | ||
665 | font-size: 130%; | ||
666 | } | ||
667 | } | ||
668 | |||
669 | list-overflow { | ||
670 | display: inline-block; | ||
671 | width: max-content; | ||
672 | } | ||
673 | } | ||
674 | } | ||
675 | |||
676 | @mixin create-button { | 617 | @mixin create-button { |
677 | @include peertube-button-link; | 618 | @include peertube-button-link; |
678 | @include orange-button; | 619 | @include orange-button; |
679 | @include button-with-icon(20px, 5px, -1px); | 620 | @include button-with-icon(20px, 5px, -1px); |
680 | } | 621 | } |
681 | 622 | ||
682 | @mixin row-blocks { | 623 | @mixin row-blocks ($column-responsive: true) { |
683 | display: flex; | 624 | display: flex; |
684 | min-height: 130px; | 625 | min-height: 130px; |
685 | padding-bottom: 20px; | 626 | padding-bottom: 20px; |
686 | margin-bottom: 20px; | 627 | margin-bottom: 20px; |
687 | border-bottom: 1px solid #C6C6C6; | 628 | border-bottom: 1px solid #C6C6C6; |
688 | 629 | ||
689 | @media screen and (max-width: 800px) { | 630 | @media screen and (max-width: $small-view) { |
690 | flex-direction: column; | 631 | @if $column-responsive { |
691 | height: auto; | 632 | flex-direction: column; |
692 | align-items: center; | 633 | height: auto; |
634 | align-items: center; | ||
635 | } @else { | ||
636 | min-height: initial; | ||
637 | padding-bottom: 10px; | ||
638 | margin-bottom: 10px; | ||
639 | } | ||
693 | } | 640 | } |
694 | } | 641 | } |
695 | 642 | ||
@@ -756,7 +703,7 @@ | |||
756 | padding: 0.75rem 1rem; | 703 | padding: 0.75rem 1rem; |
757 | margin-bottom: 1rem; | 704 | margin-bottom: 1rem; |
758 | list-style: none; | 705 | list-style: none; |
759 | background-color: pvar(--submenuColor); | 706 | background-color: pvar(--submenuBackgroundColor); |
760 | border-radius: 0.25rem; | 707 | border-radius: 0.25rem; |
761 | 708 | ||
762 | .breadcrumb-item { | 709 | .breadcrumb-item { |
@@ -811,7 +758,7 @@ | |||
811 | & > a, | 758 | & > a, |
812 | & > div { | 759 | & > div { |
813 | padding: 20px; | 760 | padding: 20px; |
814 | background: pvar(--submenuColor); | 761 | background: pvar(--submenuBackgroundColor); |
815 | border-radius: 4px; | 762 | border-radius: 4px; |
816 | box-sizing: border-box; | 763 | box-sizing: border-box; |
817 | height: 100%; | 764 | height: 100%; |
@@ -833,7 +780,7 @@ | |||
833 | } | 780 | } |
834 | } | 781 | } |
835 | 782 | ||
836 | @mixin divider($color: pvar(--submenuColor), $background: pvar(--mainBackgroundColor)) { | 783 | @mixin divider($color: pvar(--submenuBackgroundColor), $background: pvar(--mainBackgroundColor)) { |
837 | width: 95%; | 784 | width: 95%; |
838 | border-top: .05rem solid $color; | 785 | border-top: .05rem solid $color; |
839 | height: .05rem; | 786 | height: .05rem; |
@@ -916,7 +863,7 @@ | |||
916 | } | 863 | } |
917 | } | 864 | } |
918 | 865 | ||
919 | @mixin admin-sub-header-responsive ($horizontal-margins) { | 866 | @mixin admin-sub-header-responsive { |
920 | flex-direction: column; | 867 | flex-direction: column; |
921 | 868 | ||
922 | .form-sub-title { | 869 | .form-sub-title { |
@@ -931,7 +878,7 @@ | |||
931 | white-space: nowrap; | 878 | white-space: nowrap; |
932 | height: 50px; | 879 | height: 50px; |
933 | padding: 10px 0; | 880 | padding: 10px 0; |
934 | width: calc(100vw - #{$horizontal-margins*2}); | 881 | width: 100%; |
935 | 882 | ||
936 | a { | 883 | a { |
937 | margin-left: 5px; | 884 | margin-left: 5px; |
@@ -939,14 +886,16 @@ | |||
939 | } | 886 | } |
940 | } | 887 | } |
941 | 888 | ||
942 | // applies 16:9 ratio to a child element (using $selector) only using | 889 | // applies ratio (default to 16:9) to a child element (using $selector) only using |
943 | // an immediate's parent size. This allows 16:9 ratio without explicit | 890 | // an immediate's parent size. This allows to set a ratio without explicit |
944 | // dimensions, as width/height cannot be computed from each other. | 891 | // dimensions, as width/height cannot be computed from each other. |
945 | @mixin large-screen-ratio ($selector: 'div') { | 892 | @mixin block-ratio ($selector: 'div', $inverted-ratio: 9/16) { |
893 | $padding-percent: percentage($inverted-ratio); | ||
894 | |||
946 | position: relative; | 895 | position: relative; |
947 | height: 0; | 896 | height: 0; |
948 | width: 100%; | 897 | width: 100%; |
949 | padding-top: 56%; | 898 | padding-top: $padding-percent; |
950 | 899 | ||
951 | #{$selector} { | 900 | #{$selector} { |
952 | position: absolute; | 901 | position: absolute; |
@@ -991,3 +940,31 @@ | |||
991 | 940 | ||
992 | border-left: $width solid rgba(255, 255, 255, 0.95); | 941 | border-left: $width solid rgba(255, 255, 255, 0.95); |
993 | } | 942 | } |
943 | |||
944 | @mixin on-small-main-col () { | ||
945 | :host-context(.main-col:not(.expanded)) { | ||
946 | @media screen and (max-width: $small-view + $menu-width) { | ||
947 | @content; | ||
948 | } | ||
949 | } | ||
950 | |||
951 | :host-context(.main-col.expanded) { | ||
952 | @media screen and (max-width: $small-view) { | ||
953 | @content; | ||
954 | } | ||
955 | } | ||
956 | } | ||
957 | |||
958 | @mixin on-mobile-main-col () { | ||
959 | :host-context(.main-col:not(.expanded)) { | ||
960 | @media screen and (max-width: $mobile-view + $menu-width) { | ||
961 | @content; | ||
962 | } | ||
963 | } | ||
964 | |||
965 | :host-context(.main-col.expanded) { | ||
966 | @media screen and (max-width: $mobile-view) { | ||
967 | @content; | ||
968 | } | ||
969 | } | ||
970 | } | ||
diff --git a/client/src/sass/include/_variables.scss b/client/src/sass/include/_variables.scss index c8316473d..d2a5d2bd9 100644 --- a/client/src/sass/include/_variables.scss +++ b/client/src/sass/include/_variables.scss | |||
@@ -16,9 +16,10 @@ $grey-foreground-hover-color: #303030; | |||
16 | $grey-button-outline-color: scale-color($grey-foreground-color, $alpha: -95%); | 16 | $grey-button-outline-color: scale-color($grey-foreground-color, $alpha: -95%); |
17 | 17 | ||
18 | $main-color: hsl(24, 90%, 50%); | 18 | $main-color: hsl(24, 90%, 50%); |
19 | $main-hover-color: lighten($main-color, 5%); | ||
20 | $main-color-lighter: lighten($main-color, 10%); | 19 | $main-color-lighter: lighten($main-color, 10%); |
21 | $main-color-lightest: lighten($main-color, 40%); | 20 | $main-color-lightest: lighten($main-color, 40%); |
21 | $main-hover-color: lighten($main-color, 5%); | ||
22 | |||
22 | $secondary-color: hsl(187, 77%, 34%); | 23 | $secondary-color: hsl(187, 77%, 34%); |
23 | 24 | ||
24 | $support-button: inherit; | 25 | $support-button: inherit; |
@@ -47,18 +48,34 @@ $menu-bottom-color: #C6C6C6; | |||
47 | $menu-width: 240px; | 48 | $menu-width: 240px; |
48 | $menu-lateral-padding: 26px; | 49 | $menu-lateral-padding: 26px; |
49 | 50 | ||
50 | $sub-menu-color: #F7F7F7; | 51 | $sub-menu-background-color: #F7F7F7; |
51 | $sub-menu-height: 81px; | 52 | $sub-menu-height: 81px; |
52 | 53 | ||
54 | $channel-background-color: #f6ede8; | ||
55 | |||
56 | $banner-inverted-ratio: 1/6; | ||
57 | |||
58 | $max-channels-width: 1200px; | ||
59 | |||
53 | $footer-height: 30px; | 60 | $footer-height: 30px; |
54 | $footer-margin: 30px; | 61 | $footer-margin: 30px; |
55 | 62 | ||
56 | $separator-border-color: rgba(0, 0, 0, 0.10); | 63 | $separator-border-color: rgba(0, 0, 0, 0.10); |
57 | 64 | ||
58 | $video-miniature-width: 238px; | ||
59 | $video-miniature-margin-bottom: 15px; | 65 | $video-miniature-margin-bottom: 15px; |
60 | $video-thumbnail-height: 122px; | 66 | |
61 | $video-thumbnail-width: 223px; | 67 | $video-miniature-row-name-font-size: 1.3em; |
68 | $video-miniature-row-mobile-name-font-size: 14px; | ||
69 | |||
70 | $video-miniature-row-info-font-size: 14px; | ||
71 | $video-miniature-row-mobile-info-font-size: 12px; | ||
72 | |||
73 | $video-thumbnail-height: 153px; | ||
74 | $video-thumbnail-width: 280px; | ||
75 | $video-thumbnail-medium-height: 114px; | ||
76 | $video-thumbnail-medium-width: 201px; | ||
77 | $video-thumbnail-small-height: 71px; | ||
78 | $video-thumbnail-small-width: 125px; | ||
62 | 79 | ||
63 | $theater-bottom-space: 115px; | 80 | $theater-bottom-space: 115px; |
64 | 81 | ||
@@ -98,7 +115,9 @@ $variables: ( | |||
98 | 115 | ||
99 | --menuBackgroundColor: var(--menuBackgroundColor), | 116 | --menuBackgroundColor: var(--menuBackgroundColor), |
100 | --menuForegroundColor: var(--menuForegroundColor), | 117 | --menuForegroundColor: var(--menuForegroundColor), |
101 | --submenuColor: var(--submenuColor), | 118 | |
119 | --submenuBackgroundColor: var(--submenuBackgroundColor), | ||
120 | --channelBackgroundColor: var(--channelBackgroundColor), | ||
102 | 121 | ||
103 | --inputForegroundColor: var(--inputForegroundColor), | 122 | --inputForegroundColor: var(--inputForegroundColor), |
104 | --inputBackgroundColor: var(--inputBackgroundColor), | 123 | --inputBackgroundColor: var(--inputBackgroundColor), |
@@ -116,11 +135,20 @@ $variables: ( | |||
116 | --supportButtonHeartColor: var(--supportButtonHeartColor), | 135 | --supportButtonHeartColor: var(--supportButtonHeartColor), |
117 | 136 | ||
118 | --embedForegroundColor: var(--embedForegroundColor), | 137 | --embedForegroundColor: var(--embedForegroundColor), |
119 | --embedBigPlayBackgroundColor: var(--embedBigPlayBackgroundColor) | 138 | --embedBigPlayBackgroundColor: var(--embedBigPlayBackgroundColor), |
139 | |||
140 | --horizontalMarginContent: var(--horizontalMarginContent), | ||
141 | --videosHorizontalMarginContent: var(--videosHorizontalMarginContent), | ||
142 | --mainColWidth: var(--mainColWidth) | ||
120 | ); | 143 | ); |
121 | 144 | ||
145 | // SASS type check our CSS variables | ||
122 | @function pvar($variable) { | 146 | @function pvar($variable) { |
123 | @return map-get($variables, $variable); | 147 | @if map-has-key($variables, $variable) { |
148 | @return map-get($variables, $variable); | ||
149 | } @else { | ||
150 | @error "ERROR: Variable #{$variable} does not exist"; | ||
151 | } | ||
124 | } | 152 | } |
125 | 153 | ||
126 | /*** z-index groups ***/ | 154 | /*** z-index groups ***/ |
diff --git a/client/src/sass/ng-select.scss b/client/src/sass/ng-select.scss index 54c805ccf..13fc1d6c2 100644 --- a/client/src/sass/ng-select.scss +++ b/client/src/sass/ng-select.scss | |||
@@ -11,7 +11,7 @@ $ng-select-highlight: #f2690d; | |||
11 | $ng-select-box-shadow: #{$focus-box-shadow-form} pvar(--mainColorLightest); | 11 | $ng-select-box-shadow: #{$focus-box-shadow-form} pvar(--mainColorLightest); |
12 | // $ng-select-placeholder: lighten($ng-select-primary-text, 40) !default; | 12 | // $ng-select-placeholder: lighten($ng-select-primary-text, 40) !default; |
13 | $ng-select-height: 30px; | 13 | $ng-select-height: 30px; |
14 | // $ng-select-value-padding-left: 10px !default; | 14 | $ng-select-value-padding-left: 15px; |
15 | // $ng-select-value-font-size: 0.9em !default; | 15 | // $ng-select-value-font-size: 0.9em !default; |
16 | 16 | ||
17 | @import "~@ng-select/ng-select/scss/default.theme.scss"; | 17 | @import "~@ng-select/ng-select/scss/default.theme.scss"; |
@@ -20,11 +20,6 @@ $ng-select-height: 30px; | |||
20 | font-size: .9em; | 20 | font-size: .9em; |
21 | } | 21 | } |
22 | 22 | ||
23 | .ng-input, | ||
24 | .ng-select .ng-select-container .ng-value-container { | ||
25 | padding-left: 15px !important; | ||
26 | } | ||
27 | |||
28 | .ng-select { | 23 | .ng-select { |
29 | &.ng-select-focused { | 24 | &.ng-select-focused { |
30 | &:not(.ng-select-opened) > .ng-select-container { | 25 | &:not(.ng-select-opened) > .ng-select-container { |
@@ -44,4 +39,11 @@ $ng-select-height: 30px; | |||
44 | &.ng-select-single .ng-value-container .ng-value { | 39 | &.ng-select-single .ng-value-container .ng-value { |
45 | color: pvar(--inputForegroundColor); | 40 | color: pvar(--inputForegroundColor); |
46 | } | 41 | } |
42 | |||
43 | &.ng-select-multiple .ng-select-container .ng-value-container { | ||
44 | padding-left: 12px; | ||
45 | .ng-value { | ||
46 | margin-left: 3px; | ||
47 | } | ||
48 | } | ||
47 | } | 49 | } |
diff --git a/client/src/sass/player/context-menu.scss b/client/src/sass/player/context-menu.scss index f3a28ead0..ad673eea7 100644 --- a/client/src/sass/player/context-menu.scss +++ b/client/src/sass/player/context-menu.scss | |||
@@ -14,7 +14,7 @@ $context-menu-width: 350px; | |||
14 | 14 | ||
15 | .vjs-menu-content { | 15 | .vjs-menu-content { |
16 | opacity: $primary-foreground-opacity; | 16 | opacity: $primary-foreground-opacity; |
17 | color: pvar(--embedForegroundCsolor); | 17 | color: pvar(--embedForegroundColor); |
18 | font-size: $font-size !important; | 18 | font-size: $font-size !important; |
19 | font-weight: $font-semibold; | 19 | font-weight: $font-semibold; |
20 | } | 20 | } |
diff --git a/client/src/sass/player/peertube-skin.scss b/client/src/sass/player/peertube-skin.scss index 0144e89fb..81aacf1d7 100644 --- a/client/src/sass/player/peertube-skin.scss +++ b/client/src/sass/player/peertube-skin.scss | |||
@@ -43,10 +43,6 @@ body { | |||
43 | } | 43 | } |
44 | } | 44 | } |
45 | 45 | ||
46 | .vjs-button > .vjs-icon-placeholder::before { | ||
47 | line-height: $control-bar-height; | ||
48 | } | ||
49 | |||
50 | .vjs-volume-level::before { | 46 | .vjs-volume-level::before { |
51 | content: ''; /* Remove Circle From Progress Bar */ | 47 | content: ''; /* Remove Circle From Progress Bar */ |
52 | } | 48 | } |
@@ -242,8 +238,19 @@ body { | |||
242 | @include disable-outline; | 238 | @include disable-outline; |
243 | 239 | ||
244 | cursor: pointer; | 240 | cursor: pointer; |
245 | font-size: $play-control-font-size; | ||
246 | width: 2em; | 241 | width: 2em; |
242 | |||
243 | .vjs-icon-placeholder { | ||
244 | line-height: $control-bar-height; | ||
245 | position: relative; | ||
246 | top: -1px; | ||
247 | |||
248 | &::before { | ||
249 | font-size: 28px; | ||
250 | line-height: unset; | ||
251 | position: relative; | ||
252 | } | ||
253 | } | ||
247 | } | 254 | } |
248 | 255 | ||
249 | .vjs-time-control { | 256 | .vjs-time-control { |
@@ -375,7 +382,6 @@ body { | |||
375 | .vjs-mute-control { | 382 | .vjs-mute-control { |
376 | @include disable-outline; | 383 | @include disable-outline; |
377 | 384 | ||
378 | line-height: $control-bar-height; | ||
379 | padding: 0; | 385 | padding: 0; |
380 | width: 30px; | 386 | width: 30px; |
381 | 387 | ||
diff --git a/client/src/sass/primeng-custom.scss b/client/src/sass/primeng-custom.scss index afa577819..9c9b5d4fc 100644 --- a/client/src/sass/primeng-custom.scss +++ b/client/src/sass/primeng-custom.scss | |||
@@ -547,7 +547,7 @@ p-table { | |||
547 | height: 46px; | 547 | height: 46px; |
548 | 548 | ||
549 | &.p-highlight { | 549 | &.p-highlight { |
550 | background-color: pvar(--submenuColor) !important; | 550 | background-color: pvar(--submenuBackgroundColor) !important; |
551 | 551 | ||
552 | td, td > a { | 552 | td, td > a { |
553 | color: pvar(--mainForegroundColor) !important; | 553 | color: pvar(--mainForegroundColor) !important; |
@@ -558,7 +558,7 @@ p-table { | |||
558 | .p-datatable-tbody { | 558 | .p-datatable-tbody { |
559 | tr { | 559 | tr { |
560 | &:hover { | 560 | &:hover { |
561 | background-color: pvar(--submenuColor) !important; | 561 | background-color: pvar(--submenuBackgroundColor) !important; |
562 | } | 562 | } |
563 | 563 | ||
564 | td { | 564 | td { |
@@ -590,16 +590,16 @@ p-table { | |||
590 | th { | 590 | th { |
591 | border: none !important; | 591 | border: none !important; |
592 | border-bottom: 1px solid !important; | 592 | border-bottom: 1px solid !important; |
593 | border-color: pvar(--submenuColor) !important; | 593 | border-color: pvar(--submenuBackgroundColor) !important; |
594 | text-align: left !important; | 594 | text-align: left !important; |
595 | padding: 5px 0 5px 15px !important; | 595 | padding: 5px 0 5px 15px !important; |
596 | font-weight: $font-semibold !important; | 596 | font-weight: $font-semibold !important; |
597 | color: pvar(--mainForegroundColor) !important; | 597 | color: pvar(--mainForegroundColor) !important; |
598 | 598 | ||
599 | &.p-sortable-column:hover { | 599 | &.p-sortable-column:hover { |
600 | background-color: pvar(--submenuColor) !important; | 600 | background-color: pvar(--submenuBackgroundColor) !important; |
601 | border: 1px solid !important; | 601 | border: 1px solid !important; |
602 | border-color: pvar(--submenuColor) !important; | 602 | border-color: pvar(--submenuBackgroundColor) !important; |
603 | border-width: 0 1px !important; | 603 | border-width: 0 1px !important; |
604 | 604 | ||
605 | &:first-child { | 605 | &:first-child { |
@@ -608,7 +608,7 @@ p-table { | |||
608 | } | 608 | } |
609 | 609 | ||
610 | &.p-highlight { | 610 | &.p-highlight { |
611 | background-color: pvar(--submenuColor) !important; | 611 | background-color: pvar(--submenuBackgroundColor) !important; |
612 | 612 | ||
613 | .pi { | 613 | .pi { |
614 | @extend .glyphicon; | 614 | @extend .glyphicon; |
@@ -654,7 +654,7 @@ p-table { | |||
654 | position: relative; | 654 | position: relative; |
655 | border: none; | 655 | border: none; |
656 | border-top: 1px solid !important; | 656 | border-top: 1px solid !important; |
657 | border-color: pvar(--submenuColor) !important; | 657 | border-color: pvar(--submenuBackgroundColor) !important; |
658 | height: 40px; | 658 | height: 40px; |
659 | display: flex; | 659 | display: flex; |
660 | justify-content: center; | 660 | justify-content: center; |
@@ -753,29 +753,32 @@ p-table { | |||
753 | } | 753 | } |
754 | 754 | ||
755 | // overflow data table | 755 | // overflow data table |
756 | @mixin overflow-datatable ($table-min-width, $horizontal-margins, $mobile-paginator: true) { | 756 | p-table { |
757 | p-table { | 757 | .p-datatable-wrapper { |
758 | .p-datatable-wrapper { | 758 | overflow-x: auto; |
759 | overflow-x: auto; | 759 | max-width: 100%; |
760 | max-width: calc(100vw - #{$horizontal-margins * 2}); | ||
761 | |||
762 | table { | ||
763 | min-width: $table-min-width; | ||
764 | } | ||
765 | } | ||
766 | 760 | ||
767 | @if $mobile-paginator { | 761 | table { |
768 | p-paginator .p-paginator-bottom { | 762 | min-width: breakpoint(lg); |
769 | display: block; | 763 | } |
764 | } | ||
770 | 765 | ||
771 | .p-paginator-current { | 766 | @media screen and (max-width: #{breakpoint(lg)}) { |
772 | position: relative; | 767 | // Prevent overflow |
773 | display: block; | 768 | p-paginator { |
774 | } | 769 | .p-paginator-current, |
770 | .p-dropdown { | ||
771 | top: 0; | ||
772 | margin-top: 30px; | ||
773 | } | ||
774 | } | ||
775 | } | ||
775 | 776 | ||
776 | a, .p-paginator-pages { | 777 | @media screen and (max-width: $mobile-view) { |
777 | vertical-align: middle; | 778 | // Prevent overflow |
778 | } | 779 | p-paginator { |
780 | .p-paginator-pages > .p-paginator-page:not(.p-highlight) { | ||
781 | display: none; | ||
779 | } | 782 | } |
780 | } | 783 | } |
781 | } | 784 | } |
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index cf4bc6f03..ae8f176b7 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts | |||
@@ -531,6 +531,7 @@ export class PeerTubeEmbed { | |||
531 | videoCaptions, | 531 | videoCaptions, |
532 | inactivityTimeout: 2500, | 532 | inactivityTimeout: 2500, |
533 | videoViewUrl: this.getVideoUrl(videoInfo.uuid) + '/views', | 533 | videoViewUrl: this.getVideoUrl(videoInfo.uuid) + '/views', |
534 | videoUUID: videoInfo.uuid, | ||
534 | 535 | ||
535 | isLive: videoInfo.isLive, | 536 | isLive: videoInfo.isLive, |
536 | 537 | ||
@@ -545,7 +546,8 @@ export class PeerTubeEmbed { | |||
545 | 546 | ||
546 | serverUrl: window.location.origin, | 547 | serverUrl: window.location.origin, |
547 | language: navigator.language, | 548 | language: navigator.language, |
548 | embedUrl: window.location.origin + videoInfo.embedPath | 549 | embedUrl: window.location.origin + videoInfo.embedPath, |
550 | embedTitle: videoInfo.name | ||
549 | }, | 551 | }, |
550 | 552 | ||
551 | webtorrent: { | 553 | webtorrent: { |
@@ -783,6 +785,8 @@ export class PeerTubeEmbed { | |||
783 | 785 | ||
784 | showModal: unimplemented, | 786 | showModal: unimplemented, |
785 | 787 | ||
788 | getServerConfig: unimplemented, | ||
789 | |||
786 | markdownRenderer: { | 790 | markdownRenderer: { |
787 | textMarkdownToHTML: unimplemented, | 791 | textMarkdownToHTML: unimplemented, |
788 | enhancedMarkdownToHTML: unimplemented | 792 | enhancedMarkdownToHTML: unimplemented |
diff --git a/client/src/types/register-client-option.model.ts b/client/src/types/register-client-option.model.ts index e3c6d803d..7e5356a2b 100644 --- a/client/src/types/register-client-option.model.ts +++ b/client/src/types/register-client-option.model.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { RegisterClientFormFieldOptions, RegisterClientVideoFieldOptions } from '@shared/models/plugins/register-client-form-field.model' | 1 | import { RegisterClientFormFieldOptions, RegisterClientVideoFieldOptions } from '@shared/models/plugins/register-client-form-field.model' |
2 | import { RegisterClientHookOptions } from '@shared/models/plugins/register-client-hook.model' | 2 | import { RegisterClientHookOptions } from '@shared/models/plugins/register-client-hook.model' |
3 | import { ServerConfig } from '@shared/models/server' | ||
3 | 4 | ||
4 | export type RegisterClientOptions = { | 5 | export type RegisterClientOptions = { |
5 | registerHook: (options: RegisterClientHookOptions) => void | 6 | registerHook: (options: RegisterClientHookOptions) => void |
@@ -16,6 +17,8 @@ export type RegisterClientHelpers = { | |||
16 | 17 | ||
17 | getSettings: () => Promise<{ [ name: string ]: string }> | 18 | getSettings: () => Promise<{ [ name: string ]: string }> |
18 | 19 | ||
20 | getServerConfig: () => Promise<ServerConfig> | ||
21 | |||
19 | notifier: { | 22 | notifier: { |
20 | info: (text: string, title?: string, timeout?: number) => void, | 23 | info: (text: string, title?: string, timeout?: number) => void, |
21 | error: (text: string, title?: string, timeout?: number) => void, | 24 | error: (text: string, title?: string, timeout?: number) => void, |
diff --git a/client/yarn.lock b/client/yarn.lock index 79ab1e2a8..75548e83f 100644 --- a/client/yarn.lock +++ b/client/yarn.lock | |||
@@ -2,23 +2,23 @@ | |||
2 | # yarn lockfile v1 | 2 | # yarn lockfile v1 |
3 | 3 | ||
4 | 4 | ||
5 | "@angular-devkit/architect@0.1102.2": | 5 | "@angular-devkit/architect@0.1102.5": |
6 | version "0.1102.2" | 6 | version "0.1102.5" |
7 | resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1102.2.tgz#3b3eb654ae7c8c204b248bba76982ce8de2f7b6c" | 7 | resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1102.5.tgz#431df157af0c6477e5951f64ff12f3d5d5f075ee" |
8 | integrity sha512-FE7DeT13elqDlELF23QqvEFnT2BkxeC5t31/QW85IN/OR5Tf/q7XEpj7giJXyzKFQ60M3ZzbznZyRz0EqtfaBQ== | 8 | integrity sha512-lVc6NmEAZZPzvc18GzMFLoxqKKvPlNOg4vEtFsFldZmrydLJJGFi4KAs2WaJd8qVR1XuY4el841cjDQAJSq6sQ== |
9 | dependencies: | 9 | dependencies: |
10 | "@angular-devkit/core" "11.2.2" | 10 | "@angular-devkit/core" "11.2.5" |
11 | rxjs "6.6.3" | 11 | rxjs "6.6.3" |
12 | 12 | ||
13 | "@angular-devkit/build-angular@^0.1102.2": | 13 | "@angular-devkit/build-angular@^0.1102.2": |
14 | version "0.1102.2" | 14 | version "0.1102.5" |
15 | resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1102.2.tgz#c850818fd8bb4dd4fda6288390868475c4b3236e" | 15 | resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1102.5.tgz#7db51dfc33a8683458fa714d434f8c09fdc1f648" |
16 | integrity sha512-AjnvHrzkYTzDGzp0r5RmGoP9fyZXtaVFo0598PRusi1oWp1sW6B5FKPWw896iREOlotRXw3dsjqrGwbMcz0qyg== | 16 | integrity sha512-iAq/KbRq6kuA17rQZ67/0zQHEzpC9RzvtMZQ3wiiFsOmW5AIV5scjP7e6dn+F6vXZA44X4gCH5AUUkOLXyEtfg== |
17 | dependencies: | 17 | dependencies: |
18 | "@angular-devkit/architect" "0.1102.2" | 18 | "@angular-devkit/architect" "0.1102.5" |
19 | "@angular-devkit/build-optimizer" "0.1102.2" | 19 | "@angular-devkit/build-optimizer" "0.1102.5" |
20 | "@angular-devkit/build-webpack" "0.1102.2" | 20 | "@angular-devkit/build-webpack" "0.1102.5" |
21 | "@angular-devkit/core" "11.2.2" | 21 | "@angular-devkit/core" "11.2.5" |
22 | "@babel/core" "7.12.10" | 22 | "@babel/core" "7.12.10" |
23 | "@babel/generator" "7.12.11" | 23 | "@babel/generator" "7.12.11" |
24 | "@babel/plugin-transform-async-to-generator" "7.12.1" | 24 | "@babel/plugin-transform-async-to-generator" "7.12.1" |
@@ -26,8 +26,9 @@ | |||
26 | "@babel/preset-env" "7.12.11" | 26 | "@babel/preset-env" "7.12.11" |
27 | "@babel/runtime" "7.12.5" | 27 | "@babel/runtime" "7.12.5" |
28 | "@babel/template" "7.12.7" | 28 | "@babel/template" "7.12.7" |
29 | "@discoveryjs/json-ext" "0.5.2" | ||
29 | "@jsdevtools/coverage-istanbul-loader" "3.0.5" | 30 | "@jsdevtools/coverage-istanbul-loader" "3.0.5" |
30 | "@ngtools/webpack" "11.2.2" | 31 | "@ngtools/webpack" "11.2.5" |
31 | ansi-colors "4.1.1" | 32 | ansi-colors "4.1.1" |
32 | autoprefixer "10.2.4" | 33 | autoprefixer "10.2.4" |
33 | babel-loader "8.2.2" | 34 | babel-loader "8.2.2" |
@@ -88,30 +89,30 @@ | |||
88 | webpack-subresource-integrity "1.5.2" | 89 | webpack-subresource-integrity "1.5.2" |
89 | worker-plugin "5.0.0" | 90 | worker-plugin "5.0.0" |
90 | 91 | ||
91 | "@angular-devkit/build-optimizer@0.1102.2": | 92 | "@angular-devkit/build-optimizer@0.1102.5": |
92 | version "0.1102.2" | 93 | version "0.1102.5" |
93 | resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1102.2.tgz#a306fee0bc648983405320953f05ad1fc60b6b84" | 94 | resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1102.5.tgz#5c17d82a8c4f03ec0a14110838c2c3da6cb24dfd" |
94 | integrity sha512-TCWWqAe+pWZzLp/g2gG8Z5NC8JSgDNfyEuMBWxEUfo1Sm3BluXoz0BbmnietuhXJZ+fPAp9rLLzEGZlHvOlmOA== | 95 | integrity sha512-ujTwrevgMRNyWir4IdnJEdDRkVSLqugRpL6cU9OeqGn6Bu+zEzZQokLkMZvbw00eEKlf5Siej4hEeF1Hnx+LUA== |
95 | dependencies: | 96 | dependencies: |
96 | loader-utils "2.0.0" | 97 | loader-utils "2.0.0" |
97 | source-map "0.7.3" | 98 | source-map "0.7.3" |
98 | tslib "2.1.0" | 99 | tslib "2.1.0" |
99 | typescript "4.1.3" | 100 | typescript "4.1.5" |
100 | webpack-sources "2.2.0" | 101 | webpack-sources "2.2.0" |
101 | 102 | ||
102 | "@angular-devkit/build-webpack@0.1102.2": | 103 | "@angular-devkit/build-webpack@0.1102.5": |
103 | version "0.1102.2" | 104 | version "0.1102.5" |
104 | resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1102.2.tgz#f48501426a5d01b0610dafce33b4eb84d07181e6" | 105 | resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1102.5.tgz#e111acf7c0cbed761ae382089052a5c2dee71d96" |
105 | integrity sha512-59CBbwbdN8lI5/whuNeAZHRJxPlOmDc5ux8aJJNwWI9w54fz0ut/MLT3iuPk+WZuKlGdpS1sGkObfZwWen5kIQ== | 106 | integrity sha512-VMsi+mFwgPUQi7eEc2oKcf7X0xD0R1xfoguLS/+HGy3sfh+b7oJy3BU4+TRzDPBtGj6vWvENK2rwHFN3cBWvxA== |
106 | dependencies: | 107 | dependencies: |
107 | "@angular-devkit/architect" "0.1102.2" | 108 | "@angular-devkit/architect" "0.1102.5" |
108 | "@angular-devkit/core" "11.2.2" | 109 | "@angular-devkit/core" "11.2.5" |
109 | rxjs "6.6.3" | 110 | rxjs "6.6.3" |
110 | 111 | ||
111 | "@angular-devkit/core@11.2.2": | 112 | "@angular-devkit/core@11.2.5": |
112 | version "11.2.2" | 113 | version "11.2.5" |
113 | resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-11.2.2.tgz#c6b40f941b24d2af447831fc958b744316cd7d87" | 114 | resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-11.2.5.tgz#f9ba8288a6cc388808ee639c383dada50d64d06a" |
114 | integrity sha512-LUDO1AdIjereiMh0j5p9xJcdr9ifhbWCPxlZqfu5wHzUfhCx9gO2Lvjp6rZXQ3OedXg5IZUnyxHlzkszQOsgiw== | 115 | integrity sha512-DRFvEHRKoC+hTwcOAJqLe6UQa+bpXc/1IGCMHWEbuply0KIFIGQOlmaYwFZKixz3HdFZlmoCMcAVkAXvyaWVsQ== |
115 | dependencies: | 116 | dependencies: |
116 | ajv "6.12.6" | 117 | ajv "6.12.6" |
117 | fast-json-stable-stringify "2.1.0" | 118 | fast-json-stable-stringify "2.1.0" |
@@ -119,41 +120,41 @@ | |||
119 | rxjs "6.6.3" | 120 | rxjs "6.6.3" |
120 | source-map "0.7.3" | 121 | source-map "0.7.3" |
121 | 122 | ||
122 | "@angular-devkit/schematics@11.2.2": | 123 | "@angular-devkit/schematics@11.2.5": |
123 | version "11.2.2" | 124 | version "11.2.5" |
124 | resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-11.2.2.tgz#0c8c4b98a30f00649dcbb7794d3783b9a067209f" | 125 | resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-11.2.5.tgz#ddcb966f3f1dc910e55f03067036f1f6a01b8222" |
125 | integrity sha512-6bIxMwafz/+lwdtcshwOuFfhxTMU4RLma1uxBS34DXupMauPGl0IIXAy5cK9dXPlHLxuGsjeBiOM6eq033RLgw== | 126 | integrity sha512-7RoWgpMvhljPhW9CMz1EtqkwNnGpnsPyy0N29ClHPUq+o8wLR0hvbLBDz1fKSF7j1AwRccaQSNTj8KWsjzQJLQ== |
126 | dependencies: | 127 | dependencies: |
127 | "@angular-devkit/core" "11.2.2" | 128 | "@angular-devkit/core" "11.2.5" |
128 | ora "5.3.0" | 129 | ora "5.3.0" |
129 | rxjs "6.6.3" | 130 | rxjs "6.6.3" |
130 | 131 | ||
131 | "@angular/animations@^11.1.1": | 132 | "@angular/animations@^11.1.1": |
132 | version "11.2.3" | 133 | version "11.2.6" |
133 | resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-11.2.3.tgz#518183e5f7b8c3b304020ea86d12cc3216142cc9" | 134 | resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-11.2.6.tgz#36935bc0fe33f1486ed889f8b5e12915858ccf5a" |
134 | integrity sha512-Z6sHIeTeeZrRAW83NI7FO7THF50cPCFkkuvVah3qmCqopY6FuoHKUBEENyGzQGH69LbGFYhEppY8KM/6JtVF6Q== | 135 | integrity sha512-fci034QakkoIrFeY/uOmDvf6AupZ7ziU1FlBMs/wn4HOqwsPCofpawvFQnfj5nez1+KM5JOJ1VHmZKJupkWfgw== |
135 | dependencies: | 136 | dependencies: |
136 | tslib "^2.0.0" | 137 | tslib "^2.0.0" |
137 | 138 | ||
138 | "@angular/cdk@^11.0.0": | 139 | "@angular/cdk@^11.0.0": |
139 | version "11.2.2" | 140 | version "11.2.5" |
140 | resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-11.2.2.tgz#f541069db3f5705d8c064138f6cd94568fe1b658" | 141 | resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-11.2.5.tgz#e0cce8b28ca635b6151b834c6e1c4bc0a8dd7c04" |
141 | integrity sha512-p3lRDPlnOuJtLWEd020QOyn0ERyc1LF7OLi90hTdzMMxe9fT3v6sQJVRs8jIY3NTmpIm/pNDGi77+1/vKerLPQ== | 142 | integrity sha512-ugalSDLME5E9JlxcRR8RGlOYlaV6rIzxOVQrGRBzY2tdhMT4Ng+BFtCkq1K88AU1sTLHq54xg9Xkfn7b5W2kiA== |
142 | dependencies: | 143 | dependencies: |
143 | tslib "^2.0.0" | 144 | tslib "^2.0.0" |
144 | optionalDependencies: | 145 | optionalDependencies: |
145 | parse5 "^5.0.0" | 146 | parse5 "^5.0.0" |
146 | 147 | ||
147 | "@angular/cli@^11.1.2": | 148 | "@angular/cli@^11.1.2": |
148 | version "11.2.2" | 149 | version "11.2.5" |
149 | resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-11.2.2.tgz#ca56894f1a4d1f4e411408b8185b711614c3195a" | 150 | resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-11.2.5.tgz#3cf3e6432db41cebb364da2dcf3d44588535a34a" |
150 | integrity sha512-rOVBzDzrMuOgJY43O46/7yYbncx0egGfr+DMJDQdazePGH1H3INN/eA9gkVcVK53ztCYb9X1sbZKOs9TUhF6nw== | 151 | integrity sha512-GIwK8l6wtg/++8aDYW++LSf7v1uqDtB6so2rPjNlOm7oYk5iqM73KaorQb/1A52oxWE3IRSJLNQaSyUlWvHvSA== |
151 | dependencies: | 152 | dependencies: |
152 | "@angular-devkit/architect" "0.1102.2" | 153 | "@angular-devkit/architect" "0.1102.5" |
153 | "@angular-devkit/core" "11.2.2" | 154 | "@angular-devkit/core" "11.2.5" |
154 | "@angular-devkit/schematics" "11.2.2" | 155 | "@angular-devkit/schematics" "11.2.5" |
155 | "@schematics/angular" "11.2.2" | 156 | "@schematics/angular" "11.2.5" |
156 | "@schematics/update" "0.1102.2" | 157 | "@schematics/update" "0.1102.5" |
157 | "@yarnpkg/lockfile" "1.1.0" | 158 | "@yarnpkg/lockfile" "1.1.0" |
158 | ansi-colors "4.1.1" | 159 | ansi-colors "4.1.1" |
159 | debug "4.3.1" | 160 | debug "4.3.1" |
@@ -173,16 +174,16 @@ | |||
173 | uuid "8.3.2" | 174 | uuid "8.3.2" |
174 | 175 | ||
175 | "@angular/common@^11.1.1": | 176 | "@angular/common@^11.1.1": |
176 | version "11.2.3" | 177 | version "11.2.6" |
177 | resolved "https://registry.yarnpkg.com/@angular/common/-/common-11.2.3.tgz#e71d645fb6bdef9463f23a551cc072ef276c1d84" | 178 | resolved "https://registry.yarnpkg.com/@angular/common/-/common-11.2.6.tgz#9985b9f1b3d82588f85bb74b1967749b0134d017" |
178 | integrity sha512-51gVmr942SZtAFmhVfp7/3fcTQ+Tia7UxWjv6iUtYF3oCvTWbo/J1zki2VNSfmMNKJV8MaMq6XUw8UWbHA0sgQ== | 179 | integrity sha512-q1yR6bktd5p987gLEKiFY4CrHcmBxks9R6GcdgzGneQsucDtGESzEKdcJ0uaMXE+9teS+fQy5GvXel6DlA/J+w== |
179 | dependencies: | 180 | dependencies: |
180 | tslib "^2.0.0" | 181 | tslib "^2.0.0" |
181 | 182 | ||
182 | "@angular/compiler-cli@^11.1.1": | 183 | "@angular/compiler-cli@^11.1.1": |
183 | version "11.2.3" | 184 | version "11.2.6" |
184 | resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-11.2.3.tgz#5307215b9aa6e32d772906fd3b2960ba03a7565d" | 185 | resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-11.2.6.tgz#456844d71079df3ca3f025aaa9d9df9ed5a79006" |
185 | integrity sha512-ObQVI6q2c0VTWbsDnWJDdUZv2Jz/u1jiQNcrdtu/rjtJARaldEno9dMakN838Q6Nw4FzKUO6uYZXmnvKCUjfxQ== | 186 | integrity sha512-1OC8UkySaLzaw3aSrm8A6SA88CxQAdA4ffaOhBLE/Ee6CxpneVxn3ORlnccqnS8zWyEpschbootPJV56U3Azeg== |
186 | dependencies: | 187 | dependencies: |
187 | "@babel/core" "^7.8.6" | 188 | "@babel/core" "^7.8.6" |
188 | "@babel/types" "^7.8.6" | 189 | "@babel/types" "^7.8.6" |
@@ -206,9 +207,9 @@ | |||
206 | integrity sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ== | 207 | integrity sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ== |
207 | 208 | ||
208 | "@angular/compiler@^11.1.1": | 209 | "@angular/compiler@^11.1.1": |
209 | version "11.2.3" | 210 | version "11.2.6" |
210 | resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-11.2.3.tgz#72427d57b992bf6840fb7268357a466095caf8eb" | 211 | resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-11.2.6.tgz#8b69cd2f2c3bb0fbc6f95ded1ccbe20e6858daed" |
211 | integrity sha512-De8BwtSwPVYGdvQa6CDq2C1SLmB78YjS0t/KNlvfp85cl4Gb3BdjTDsKMkJXkm/3ubnIXi1BaRIsFNVTCCF70Q== | 212 | integrity sha512-3ijsCxnCLU1V1hy4UMf9qtMz5LR+wCdVFDqktEQccN9YEkN0cNtOc8Nu9EV9/mc2tqd1Q4xSBpb2o2mvpy7AhQ== |
212 | dependencies: | 213 | dependencies: |
213 | tslib "^2.0.0" | 214 | tslib "^2.0.0" |
214 | 215 | ||
@@ -218,53 +219,53 @@ | |||
218 | integrity sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w== | 219 | integrity sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w== |
219 | 220 | ||
220 | "@angular/core@^11.1.1": | 221 | "@angular/core@^11.1.1": |
221 | version "11.2.3" | 222 | version "11.2.6" |
222 | resolved "https://registry.yarnpkg.com/@angular/core/-/core-11.2.3.tgz#7dd59f35e0b2410543a61be6048c474c18a43f40" | 223 | resolved "https://registry.yarnpkg.com/@angular/core/-/core-11.2.6.tgz#c38ee7834519d3c94e51be62156784a984cd93d2" |
223 | integrity sha512-+G7rZj21Mcmf6nWjQ79EwomwEOVQ1WLqw6YvCXWzgJ9ZlVjLi/Sti0/jIzUpgK0E0Fn86yuXw/vgYq5kjGeOcQ== | 224 | integrity sha512-lS5JOQ/Y9gbk5WiMnCp5Zyz2pRIoZ+IWLOXHU5rkQeXy0zE3eMJhw0FfpEK+X5CeSNl2EPVSPLT0MtDtbNPodg== |
224 | dependencies: | 225 | dependencies: |
225 | tslib "^2.0.0" | 226 | tslib "^2.0.0" |
226 | 227 | ||
227 | "@angular/forms@^11.1.1": | 228 | "@angular/forms@^11.1.1": |
228 | version "11.2.3" | 229 | version "11.2.6" |
229 | resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-11.2.3.tgz#57460a110e6601b50362f878fc0f67701c76dc24" | 230 | resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-11.2.6.tgz#d82a1c655754d48ec861b9b3af370e6ee1e841cb" |
230 | integrity sha512-VfyKV8IxHTclcHQmt5gjGFmKC1kGz7sdNLYsEM+M0y88Bsufh3VIhK4kspfO4nhJxVfh6HFOt1JVQ5bvo6PDlQ== | 231 | integrity sha512-0xxayXCNc8lPQhDj5q/hAcG55cmDXPSBn2cxX4V+uDSGwKU1+h2CQID6gJdBJBh5wOaeMe6h8dK2s1pRgok66A== |
231 | dependencies: | 232 | dependencies: |
232 | tslib "^2.0.0" | 233 | tslib "^2.0.0" |
233 | 234 | ||
234 | "@angular/localize@^11.1.1": | 235 | "@angular/localize@^11.1.1": |
235 | version "11.2.3" | 236 | version "11.2.6" |
236 | resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-11.2.3.tgz#df2e605341be53c2d4cead2d8b274415af8b3136" | 237 | resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-11.2.6.tgz#465f2541c5bcdc396725504becaec3b96c718ec8" |
237 | integrity sha512-SCpum70G+MuoRitbv+u92fjDlKEbYizTosukxryh56QNa47iO3/rkVp8P2R75FDYJVJrxqoTiMGl0Q9tKdrEGA== | 238 | integrity sha512-8K+SdqKqIaRlNRegDBy//VAtf2rlwoZAmqoFfiM5ujuB4SFt32NAduxDUlFGWdZD5V3iPorFBrceq04bt695AA== |
238 | dependencies: | 239 | dependencies: |
239 | "@babel/core" "7.8.3" | 240 | "@babel/core" "7.8.3" |
240 | glob "7.1.2" | 241 | glob "7.1.2" |
241 | yargs "^16.1.1" | 242 | yargs "^16.1.1" |
242 | 243 | ||
243 | "@angular/platform-browser-dynamic@^11.1.1": | 244 | "@angular/platform-browser-dynamic@^11.1.1": |
244 | version "11.2.3" | 245 | version "11.2.6" |
245 | resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.2.3.tgz#3d7eb15ba4bcc9e227f68f13bf20258fa16efad1" | 246 | resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.2.6.tgz#26acbe4de315019ebe1e925ee826eda20c95d881" |
246 | integrity sha512-QUPCvack7De6u5AqWcW8O6FzczwqoL858R1NlnqojnNbcnN/dCtXtKvvETEEgp/9VMwLfcuLd1BWdBJSah7f6A== | 247 | integrity sha512-B56b8yPW3vAmPe4VONiBYEMZ6B1i5CUkJvit8qWWK3y7t5XrYOihIiGC0UqEDaw/uAg72GXjixspcxZWan5e9w== |
247 | dependencies: | 248 | dependencies: |
248 | tslib "^2.0.0" | 249 | tslib "^2.0.0" |
249 | 250 | ||
250 | "@angular/platform-browser@^11.1.1": | 251 | "@angular/platform-browser@^11.1.1": |
251 | version "11.2.3" | 252 | version "11.2.6" |
252 | resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-11.2.3.tgz#0c6b537500a1c6304829fab19cf8c12daa2b48b9" | 253 | resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-11.2.6.tgz#d2af4323275f501e279ee2aa821ac5599c11feae" |
253 | integrity sha512-S0IP/kGinIH18+gfnX0gLFLbP0Euw1RBceDt/WipYhUeFZZryQHvot/6KFLFtO+8rVunfrg+UyBiaK65/TT9Og== | 254 | integrity sha512-xnYpfoqWyQOUngfbHefsZMyelCSAaxpopu/WYP0gpbYh9qJiVhsN9s6zRMqOIPueq9lmvlEuGBMgaJjeD6Ei7Q== |
254 | dependencies: | 255 | dependencies: |
255 | tslib "^2.0.0" | 256 | tslib "^2.0.0" |
256 | 257 | ||
257 | "@angular/router@^11.1.1": | 258 | "@angular/router@^11.1.1": |
258 | version "11.2.3" | 259 | version "11.2.6" |
259 | resolved "https://registry.yarnpkg.com/@angular/router/-/router-11.2.3.tgz#407a0797845c1cac963663537b30872e39e4b229" | 260 | resolved "https://registry.yarnpkg.com/@angular/router/-/router-11.2.6.tgz#5845ef37e85400aeeaf0ffe670802a58569638cc" |
260 | integrity sha512-lRuEIlNj2BcBZ17mt5SZY7v80PsvlS4J6EbKSOFeSYhALM/AQnaaCdrrMlQ1WyEa5bBUabxGT9/zvahBosy2yA== | 261 | integrity sha512-n/3Sp36slXzRXUcUO9nVs3CkgFxa6U9A8GENeyxq9XQtcE912jOP4dzjDi3hlaNKbX9ijOyEh505KpqmiSYATg== |
261 | dependencies: | 262 | dependencies: |
262 | tslib "^2.0.0" | 263 | tslib "^2.0.0" |
263 | 264 | ||
264 | "@angular/service-worker@^11.1.1": | 265 | "@angular/service-worker@^11.1.1": |
265 | version "11.2.3" | 266 | version "11.2.6" |
266 | resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-11.2.3.tgz#316bfc07ccebdc5af1a9cbc825082880c551c0b9" | 267 | resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-11.2.6.tgz#65e895a7a1dc309c9365ea801806549f7572646c" |
267 | integrity sha512-/JgA4rCH2SyIK/v0+sCqNgiBEV/pXQUcUoqfm//2zfc3VwerehvF3RtRBfabtLBpdwdO5a9DZ4nX+djvTJypvw== | 268 | integrity sha512-nZGwVhHZ6eLptnPzIjiFiktnl4ImC+4kejR3AaElTX8PgS9TykhYhgENB+ILU49bZOGMe3RVnNthgx/JkIEgjQ== |
268 | dependencies: | 269 | dependencies: |
269 | tslib "^2.0.0" | 270 | tslib "^2.0.0" |
270 | 271 | ||
@@ -275,10 +276,10 @@ | |||
275 | dependencies: | 276 | dependencies: |
276 | "@babel/highlight" "^7.12.13" | 277 | "@babel/highlight" "^7.12.13" |
277 | 278 | ||
278 | "@babel/compat-data@^7.12.7", "@babel/compat-data@^7.13.0": | 279 | "@babel/compat-data@^7.12.7", "@babel/compat-data@^7.13.8": |
279 | version "7.13.6" | 280 | version "7.13.12" |
280 | resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.6.tgz#11972d07db4c2317afdbf41d6feb3a730301ef4e" | 281 | resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.12.tgz#a8a5ccac19c200f9dd49624cac6e19d7be1236a1" |
281 | integrity sha512-VhgqKOWYVm7lQXlvbJnWOzwfAQATd2nV52koT0HZ/LdDH0m4DUDwkKYsH+IwpXb+bKPyBJzawA4I6nBKqZcpQw== | 282 | integrity sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ== |
282 | 283 | ||
283 | "@babel/core@7.12.10": | 284 | "@babel/core@7.12.10": |
284 | version "7.12.10" | 285 | version "7.12.10" |
@@ -323,16 +324,16 @@ | |||
323 | source-map "^0.5.0" | 324 | source-map "^0.5.0" |
324 | 325 | ||
325 | "@babel/core@^7.7.5", "@babel/core@^7.8.6": | 326 | "@babel/core@^7.7.5", "@babel/core@^7.8.6": |
326 | version "7.13.1" | 327 | version "7.13.10" |
327 | resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.1.tgz#7ddd027176debe40f13bb88bac0c21218c5b1ecf" | 328 | resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.10.tgz#07de050bbd8193fcd8a3c27918c0890613a94559" |
328 | integrity sha512-FzeKfFBG2rmFtGiiMdXZPFt/5R5DXubVi82uYhjGX4Msf+pgYQMCFIqFXZWs5vbIYbf14VeBIgdGI03CDOOM1w== | 329 | integrity sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw== |
329 | dependencies: | 330 | dependencies: |
330 | "@babel/code-frame" "^7.12.13" | 331 | "@babel/code-frame" "^7.12.13" |
331 | "@babel/generator" "^7.13.0" | 332 | "@babel/generator" "^7.13.9" |
332 | "@babel/helper-compilation-targets" "^7.13.0" | 333 | "@babel/helper-compilation-targets" "^7.13.10" |
333 | "@babel/helper-module-transforms" "^7.13.0" | 334 | "@babel/helper-module-transforms" "^7.13.0" |
334 | "@babel/helpers" "^7.13.0" | 335 | "@babel/helpers" "^7.13.10" |
335 | "@babel/parser" "^7.13.0" | 336 | "@babel/parser" "^7.13.10" |
336 | "@babel/template" "^7.12.13" | 337 | "@babel/template" "^7.12.13" |
337 | "@babel/traverse" "^7.13.0" | 338 | "@babel/traverse" "^7.13.0" |
338 | "@babel/types" "^7.13.0" | 339 | "@babel/types" "^7.13.0" |
@@ -341,7 +342,7 @@ | |||
341 | gensync "^1.0.0-beta.2" | 342 | gensync "^1.0.0-beta.2" |
342 | json5 "^2.1.2" | 343 | json5 "^2.1.2" |
343 | lodash "^4.17.19" | 344 | lodash "^4.17.19" |
344 | semver "7.0.0" | 345 | semver "^6.3.0" |
345 | source-map "^0.5.0" | 346 | source-map "^0.5.0" |
346 | 347 | ||
347 | "@babel/generator@7.12.11": | 348 | "@babel/generator@7.12.11": |
@@ -353,10 +354,10 @@ | |||
353 | jsesc "^2.5.1" | 354 | jsesc "^2.5.1" |
354 | source-map "^0.5.0" | 355 | source-map "^0.5.0" |
355 | 356 | ||
356 | "@babel/generator@^7.12.10", "@babel/generator@^7.13.0", "@babel/generator@^7.8.3": | 357 | "@babel/generator@^7.12.10", "@babel/generator@^7.13.0", "@babel/generator@^7.13.9", "@babel/generator@^7.8.3": |
357 | version "7.13.0" | 358 | version "7.13.9" |
358 | resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.0.tgz#bd00d4394ca22f220390c56a0b5b85568ec1ec0c" | 359 | resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" |
359 | integrity sha512-zBZfgvBB/ywjx0Rgc2+BwoH/3H+lDtlgD4hBOpEv5LxRnYsm/753iRuLepqnYlynpjC3AdQxtxsoeHJoEEwOAw== | 360 | integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== |
360 | dependencies: | 361 | dependencies: |
361 | "@babel/types" "^7.13.0" | 362 | "@babel/types" "^7.13.0" |
362 | jsesc "^2.5.1" | 363 | jsesc "^2.5.1" |
@@ -377,20 +378,20 @@ | |||
377 | "@babel/helper-explode-assignable-expression" "^7.12.13" | 378 | "@babel/helper-explode-assignable-expression" "^7.12.13" |
378 | "@babel/types" "^7.12.13" | 379 | "@babel/types" "^7.12.13" |
379 | 380 | ||
380 | "@babel/helper-compilation-targets@^7.12.5", "@babel/helper-compilation-targets@^7.13.0": | 381 | "@babel/helper-compilation-targets@^7.12.5", "@babel/helper-compilation-targets@^7.13.10", "@babel/helper-compilation-targets@^7.13.8": |
381 | version "7.13.0" | 382 | version "7.13.10" |
382 | resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.0.tgz#c9cf29b82a76fd637f0faa35544c4ace60a155a1" | 383 | resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.10.tgz#1310a1678cb8427c07a753750da4f8ce442bdd0c" |
383 | integrity sha512-SOWD0JK9+MMIhTQiUVd4ng8f3NXhPVQvTv7D3UN4wbp/6cAHnB2EmMaU1zZA2Hh1gwme+THBrVSqTFxHczTh0Q== | 384 | integrity sha512-/Xju7Qg1GQO4mHZ/Kcs6Au7gfafgZnwm+a7sy/ow/tV1sHeraRUHbjdat8/UvDor4Tez+siGKDk6zIKtCPKVJA== |
384 | dependencies: | 385 | dependencies: |
385 | "@babel/compat-data" "^7.13.0" | 386 | "@babel/compat-data" "^7.13.8" |
386 | "@babel/helper-validator-option" "^7.12.17" | 387 | "@babel/helper-validator-option" "^7.12.17" |
387 | browserslist "^4.14.5" | 388 | browserslist "^4.14.5" |
388 | semver "7.0.0" | 389 | semver "^6.3.0" |
389 | 390 | ||
390 | "@babel/helper-create-class-features-plugin@^7.13.0": | 391 | "@babel/helper-create-class-features-plugin@^7.13.0": |
391 | version "7.13.0" | 392 | version "7.13.11" |
392 | resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.0.tgz#28d04ad9cfbd1ed1d8b988c9ea7b945263365846" | 393 | resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.11.tgz#30d30a005bca2c953f5653fc25091a492177f4f6" |
393 | integrity sha512-twwzhthM4/+6o9766AW2ZBHpIHPSGrPGk1+WfHiu13u/lBnggXGNYCpeAyVfNwGDKfkhEDp+WOD/xafoJ2iLjA== | 394 | integrity sha512-ays0I7XYq9xbjCSvT+EvysLgfc3tOkwCULHjrnscGT3A9qD4sk3wXnJ3of0MAWsWGjdinFvajHU2smYuqXKMrw== |
394 | dependencies: | 395 | dependencies: |
395 | "@babel/helper-function-name" "^7.12.13" | 396 | "@babel/helper-function-name" "^7.12.13" |
396 | "@babel/helper-member-expression-to-functions" "^7.13.0" | 397 | "@babel/helper-member-expression-to-functions" "^7.13.0" |
@@ -429,7 +430,7 @@ | |||
429 | dependencies: | 430 | dependencies: |
430 | "@babel/types" "^7.12.13" | 431 | "@babel/types" "^7.12.13" |
431 | 432 | ||
432 | "@babel/helper-hoist-variables@^7.12.13": | 433 | "@babel/helper-hoist-variables@^7.13.0": |
433 | version "7.13.0" | 434 | version "7.13.0" |
434 | resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.0.tgz#5d5882e855b5c5eda91e0cadc26c6e7a2c8593d8" | 435 | resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.0.tgz#5d5882e855b5c5eda91e0cadc26c6e7a2c8593d8" |
435 | integrity sha512-0kBzvXiIKfsCA0y6cFEIJf4OdzfpRuNk4+YTeHZpGGc666SATFKTz6sRncwFnQk7/ugJ4dSrCj6iJuvW4Qwr2g== | 436 | integrity sha512-0kBzvXiIKfsCA0y6cFEIJf4OdzfpRuNk4+YTeHZpGGc666SATFKTz6sRncwFnQk7/ugJ4dSrCj6iJuvW4Qwr2g== |
@@ -437,34 +438,33 @@ | |||
437 | "@babel/traverse" "^7.13.0" | 438 | "@babel/traverse" "^7.13.0" |
438 | "@babel/types" "^7.13.0" | 439 | "@babel/types" "^7.13.0" |
439 | 440 | ||
440 | "@babel/helper-member-expression-to-functions@^7.13.0": | 441 | "@babel/helper-member-expression-to-functions@^7.13.0", "@babel/helper-member-expression-to-functions@^7.13.12": |
441 | version "7.13.0" | 442 | version "7.13.12" |
442 | resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.0.tgz#6aa4bb678e0f8c22f58cdb79451d30494461b091" | 443 | resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" |
443 | integrity sha512-yvRf8Ivk62JwisqV1rFRMxiSMDGnN6KH1/mDMmIrij4jztpQNRoHqqMG3U6apYbGRPJpgPalhva9Yd06HlUxJQ== | 444 | integrity sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw== |
444 | dependencies: | 445 | dependencies: |
445 | "@babel/types" "^7.13.0" | 446 | "@babel/types" "^7.13.12" |
446 | 447 | ||
447 | "@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.12.5": | 448 | "@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.12.5", "@babel/helper-module-imports@^7.13.12": |
448 | version "7.12.13" | 449 | version "7.13.12" |
449 | resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz#ec67e4404f41750463e455cc3203f6a32e93fcb0" | 450 | resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977" |
450 | integrity sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g== | 451 | integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA== |
451 | dependencies: | 452 | dependencies: |
452 | "@babel/types" "^7.12.13" | 453 | "@babel/types" "^7.13.12" |
453 | 454 | ||
454 | "@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.12.13", "@babel/helper-module-transforms@^7.13.0": | 455 | "@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.13.0": |
455 | version "7.13.0" | 456 | version "7.13.12" |
456 | resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz#42eb4bd8eea68bab46751212c357bfed8b40f6f1" | 457 | resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.12.tgz#600e58350490828d82282631a1422268e982ba96" |
457 | integrity sha512-Ls8/VBwH577+pw7Ku1QkUWIyRRNHpYlts7+qSqBBFCW3I8QteB9DxfcZ5YJpOwH6Ihe/wn8ch7fMGOP1OhEIvw== | 458 | integrity sha512-7zVQqMO3V+K4JOOj40kxiCrMf6xlQAkewBB0eu2b03OO/Q21ZutOzjpfD79A5gtE/2OWi1nv625MrDlGlkbknQ== |
458 | dependencies: | 459 | dependencies: |
459 | "@babel/helper-module-imports" "^7.12.13" | 460 | "@babel/helper-module-imports" "^7.13.12" |
460 | "@babel/helper-replace-supers" "^7.13.0" | 461 | "@babel/helper-replace-supers" "^7.13.12" |
461 | "@babel/helper-simple-access" "^7.12.13" | 462 | "@babel/helper-simple-access" "^7.13.12" |
462 | "@babel/helper-split-export-declaration" "^7.12.13" | 463 | "@babel/helper-split-export-declaration" "^7.12.13" |
463 | "@babel/helper-validator-identifier" "^7.12.11" | 464 | "@babel/helper-validator-identifier" "^7.12.11" |
464 | "@babel/template" "^7.12.13" | 465 | "@babel/template" "^7.12.13" |
465 | "@babel/traverse" "^7.13.0" | 466 | "@babel/traverse" "^7.13.0" |
466 | "@babel/types" "^7.13.0" | 467 | "@babel/types" "^7.13.12" |
467 | lodash "^4.17.19" | ||
468 | 468 | ||
469 | "@babel/helper-optimise-call-expression@^7.12.13": | 469 | "@babel/helper-optimise-call-expression@^7.12.13": |
470 | version "7.12.13" | 470 | version "7.12.13" |
@@ -487,22 +487,22 @@ | |||
487 | "@babel/helper-wrap-function" "^7.13.0" | 487 | "@babel/helper-wrap-function" "^7.13.0" |
488 | "@babel/types" "^7.13.0" | 488 | "@babel/types" "^7.13.0" |
489 | 489 | ||
490 | "@babel/helper-replace-supers@^7.12.13", "@babel/helper-replace-supers@^7.13.0": | 490 | "@babel/helper-replace-supers@^7.12.13", "@babel/helper-replace-supers@^7.13.0", "@babel/helper-replace-supers@^7.13.12": |
491 | version "7.13.0" | 491 | version "7.13.12" |
492 | resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.0.tgz#6034b7b51943094cb41627848cb219cb02be1d24" | 492 | resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz#6442f4c1ad912502481a564a7386de0c77ff3804" |
493 | integrity sha512-Segd5me1+Pz+rmN/NFBOplMbZG3SqRJOBlY+mA0SxAv6rjj7zJqr1AVr3SfzUVTLCv7ZLU5FycOM/SBGuLPbZw== | 493 | integrity sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw== |
494 | dependencies: | 494 | dependencies: |
495 | "@babel/helper-member-expression-to-functions" "^7.13.0" | 495 | "@babel/helper-member-expression-to-functions" "^7.13.12" |
496 | "@babel/helper-optimise-call-expression" "^7.12.13" | 496 | "@babel/helper-optimise-call-expression" "^7.12.13" |
497 | "@babel/traverse" "^7.13.0" | 497 | "@babel/traverse" "^7.13.0" |
498 | "@babel/types" "^7.13.0" | 498 | "@babel/types" "^7.13.12" |
499 | 499 | ||
500 | "@babel/helper-simple-access@^7.12.13": | 500 | "@babel/helper-simple-access@^7.12.13", "@babel/helper-simple-access@^7.13.12": |
501 | version "7.12.13" | 501 | version "7.13.12" |
502 | resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz#8478bcc5cacf6aa1672b251c1d2dde5ccd61a6c4" | 502 | resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6" |
503 | integrity sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA== | 503 | integrity sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA== |
504 | dependencies: | 504 | dependencies: |
505 | "@babel/types" "^7.12.13" | 505 | "@babel/types" "^7.13.12" |
506 | 506 | ||
507 | "@babel/helper-skip-transparent-expression-wrappers@^7.12.1": | 507 | "@babel/helper-skip-transparent-expression-wrappers@^7.12.1": |
508 | version "7.12.1" | 508 | version "7.12.1" |
@@ -538,37 +538,37 @@ | |||
538 | "@babel/traverse" "^7.13.0" | 538 | "@babel/traverse" "^7.13.0" |
539 | "@babel/types" "^7.13.0" | 539 | "@babel/types" "^7.13.0" |
540 | 540 | ||
541 | "@babel/helpers@^7.12.5", "@babel/helpers@^7.13.0", "@babel/helpers@^7.8.3": | 541 | "@babel/helpers@^7.12.5", "@babel/helpers@^7.13.10", "@babel/helpers@^7.8.3": |
542 | version "7.13.0" | 542 | version "7.13.10" |
543 | resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.0.tgz#7647ae57377b4f0408bf4f8a7af01c42e41badc0" | 543 | resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.10.tgz#fd8e2ba7488533cdeac45cc158e9ebca5e3c7df8" |
544 | integrity sha512-aan1MeFPxFacZeSz6Ld7YZo5aPuqnKlD7+HZY75xQsueczFccP9A7V05+oe0XpLwHK3oLorPe9eaAUljL7WEaQ== | 544 | integrity sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ== |
545 | dependencies: | 545 | dependencies: |
546 | "@babel/template" "^7.12.13" | 546 | "@babel/template" "^7.12.13" |
547 | "@babel/traverse" "^7.13.0" | 547 | "@babel/traverse" "^7.13.0" |
548 | "@babel/types" "^7.13.0" | 548 | "@babel/types" "^7.13.0" |
549 | 549 | ||
550 | "@babel/highlight@^7.12.13": | 550 | "@babel/highlight@^7.12.13": |
551 | version "7.12.13" | 551 | version "7.13.10" |
552 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.12.13.tgz#8ab538393e00370b26271b01fa08f7f27f2e795c" | 552 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1" |
553 | integrity sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww== | 553 | integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg== |
554 | dependencies: | 554 | dependencies: |
555 | "@babel/helper-validator-identifier" "^7.12.11" | 555 | "@babel/helper-validator-identifier" "^7.12.11" |
556 | chalk "^2.0.0" | 556 | chalk "^2.0.0" |
557 | js-tokens "^4.0.0" | 557 | js-tokens "^4.0.0" |
558 | 558 | ||
559 | "@babel/parser@^7.12.10", "@babel/parser@^7.12.13", "@babel/parser@^7.12.7", "@babel/parser@^7.13.0", "@babel/parser@^7.8.3": | 559 | "@babel/parser@^7.12.10", "@babel/parser@^7.12.13", "@babel/parser@^7.12.7", "@babel/parser@^7.13.0", "@babel/parser@^7.13.10", "@babel/parser@^7.8.3": |
560 | version "7.13.4" | 560 | version "7.13.12" |
561 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.4.tgz#340211b0da94a351a6f10e63671fa727333d13ab" | 561 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.12.tgz#ba320059420774394d3b0c0233ba40e4250b81d1" |
562 | integrity sha512-uvoOulWHhI+0+1f9L4BoozY7U5cIkZ9PgJqvb041d6vypgUmtVPG4vmGm4pSggjl8BELzvHyUeJSUyEMY6b+qA== | 562 | integrity sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw== |
563 | 563 | ||
564 | "@babel/plugin-proposal-async-generator-functions@^7.12.1": | 564 | "@babel/plugin-proposal-async-generator-functions@^7.12.1": |
565 | version "7.13.5" | 565 | version "7.13.8" |
566 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.5.tgz#69e3fbb9958949b09036e27b26eba1aafa1ba3db" | 566 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.8.tgz#87aacb574b3bc4b5603f6fe41458d72a5a2ec4b1" |
567 | integrity sha512-8cErJEDzhZgNKzYyjCKsHuyPqtWxG8gc9h4OFSUDJu0vCAOsObPU2LcECnW0kJwh/b+uUz46lObVzIXw0fzAbA== | 567 | integrity sha512-rPBnhj+WgoSmgq+4gQUtXx/vOcU+UYtjy1AA/aeD61Hwj410fwYyqfUcRP3lR8ucgliVJL/G7sXcNUecC75IXA== |
568 | dependencies: | 568 | dependencies: |
569 | "@babel/helper-plugin-utils" "^7.13.0" | 569 | "@babel/helper-plugin-utils" "^7.13.0" |
570 | "@babel/helper-remap-async-to-generator" "^7.13.0" | 570 | "@babel/helper-remap-async-to-generator" "^7.13.0" |
571 | "@babel/plugin-syntax-async-generators" "^7.8.0" | 571 | "@babel/plugin-syntax-async-generators" "^7.8.4" |
572 | 572 | ||
573 | "@babel/plugin-proposal-class-properties@^7.12.1": | 573 | "@babel/plugin-proposal-class-properties@^7.12.1": |
574 | version "7.13.0" | 574 | version "7.13.0" |
@@ -579,12 +579,12 @@ | |||
579 | "@babel/helper-plugin-utils" "^7.13.0" | 579 | "@babel/helper-plugin-utils" "^7.13.0" |
580 | 580 | ||
581 | "@babel/plugin-proposal-dynamic-import@^7.12.1": | 581 | "@babel/plugin-proposal-dynamic-import@^7.12.1": |
582 | version "7.12.17" | 582 | version "7.13.8" |
583 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.17.tgz#e0ebd8db65acc37eac518fa17bead2174e224512" | 583 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz#876a1f6966e1dec332e8c9451afda3bebcdf2e1d" |
584 | integrity sha512-ZNGoFZqrnuy9H2izB2jLlnNDAfVPlGl5NhFEiFe4D84ix9GQGygF+CWMGHKuE+bpyS/AOuDQCnkiRNqW2IzS1Q== | 584 | integrity sha512-ONWKj0H6+wIRCkZi9zSbZtE/r73uOhMVHh256ys0UzfM7I3d4n+spZNWjOnJv2gzopumP2Wxi186vI8N0Y2JyQ== |
585 | dependencies: | 585 | dependencies: |
586 | "@babel/helper-plugin-utils" "^7.12.13" | 586 | "@babel/helper-plugin-utils" "^7.13.0" |
587 | "@babel/plugin-syntax-dynamic-import" "^7.8.0" | 587 | "@babel/plugin-syntax-dynamic-import" "^7.8.3" |
588 | 588 | ||
589 | "@babel/plugin-proposal-export-namespace-from@^7.12.1": | 589 | "@babel/plugin-proposal-export-namespace-from@^7.12.1": |
590 | version "7.12.13" | 590 | version "7.12.13" |
@@ -595,28 +595,28 @@ | |||
595 | "@babel/plugin-syntax-export-namespace-from" "^7.8.3" | 595 | "@babel/plugin-syntax-export-namespace-from" "^7.8.3" |
596 | 596 | ||
597 | "@babel/plugin-proposal-json-strings@^7.12.1": | 597 | "@babel/plugin-proposal-json-strings@^7.12.1": |
598 | version "7.12.13" | 598 | version "7.13.8" |
599 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.13.tgz#ced7888a2db92a3d520a2e35eb421fdb7fcc9b5d" | 599 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz#bf1fb362547075afda3634ed31571c5901afef7b" |
600 | integrity sha512-v9eEi4GiORDg8x+Dmi5r8ibOe0VXoKDeNPYcTTxdGN4eOWikrJfDJCJrr1l5gKGvsNyGJbrfMftC2dTL6oz7pg== | 600 | integrity sha512-w4zOPKUFPX1mgvTmL/fcEqy34hrQ1CRcGxdphBc6snDnnqJ47EZDIyop6IwXzAC8G916hsIuXB2ZMBCExC5k7Q== |
601 | dependencies: | 601 | dependencies: |
602 | "@babel/helper-plugin-utils" "^7.12.13" | 602 | "@babel/helper-plugin-utils" "^7.13.0" |
603 | "@babel/plugin-syntax-json-strings" "^7.8.0" | 603 | "@babel/plugin-syntax-json-strings" "^7.8.3" |
604 | 604 | ||
605 | "@babel/plugin-proposal-logical-assignment-operators@^7.12.1": | 605 | "@babel/plugin-proposal-logical-assignment-operators@^7.12.1": |
606 | version "7.12.13" | 606 | version "7.13.8" |
607 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.13.tgz#575b5d9a08d8299eeb4db6430da6e16e5cf14350" | 607 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz#93fa78d63857c40ce3c8c3315220fd00bfbb4e1a" |
608 | integrity sha512-fqmiD3Lz7jVdK6kabeSr1PZlWSUVqSitmHEe3Z00dtGTKieWnX9beafvavc32kjORa5Bai4QNHgFDwWJP+WtSQ== | 608 | integrity sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A== |
609 | dependencies: | 609 | dependencies: |
610 | "@babel/helper-plugin-utils" "^7.12.13" | 610 | "@babel/helper-plugin-utils" "^7.13.0" |
611 | "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" | 611 | "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" |
612 | 612 | ||
613 | "@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1": | 613 | "@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1": |
614 | version "7.13.0" | 614 | version "7.13.8" |
615 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.0.tgz#1a96fdf2c43109cfe5568513c5379015a23f5380" | 615 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz#3730a31dafd3c10d8ccd10648ed80a2ac5472ef3" |
616 | integrity sha512-UkAvFA/9+lBBL015gjA68NvKiCReNxqFLm3SdNKaM3XXoDisA7tMAIX4PmIwatFoFqMxxT3WyG9sK3MO0Kting== | 616 | integrity sha512-iePlDPBn//UhxExyS9KyeYU7RM9WScAG+D3Hhno0PLJebAEpDZMocbDe64eqynhNAnwz/vZoL/q/QB2T1OH39A== |
617 | dependencies: | 617 | dependencies: |
618 | "@babel/helper-plugin-utils" "^7.13.0" | 618 | "@babel/helper-plugin-utils" "^7.13.0" |
619 | "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" | 619 | "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" |
620 | 620 | ||
621 | "@babel/plugin-proposal-numeric-separator@^7.12.7": | 621 | "@babel/plugin-proposal-numeric-separator@^7.12.7": |
622 | version "7.12.13" | 622 | version "7.12.13" |
@@ -627,30 +627,32 @@ | |||
627 | "@babel/plugin-syntax-numeric-separator" "^7.10.4" | 627 | "@babel/plugin-syntax-numeric-separator" "^7.10.4" |
628 | 628 | ||
629 | "@babel/plugin-proposal-object-rest-spread@^7.12.1": | 629 | "@babel/plugin-proposal-object-rest-spread@^7.12.1": |
630 | version "7.13.0" | 630 | version "7.13.8" |
631 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.0.tgz#8f19ad247bb96bd5ad2d4107e6eddfe0a789937b" | 631 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz#5d210a4d727d6ce3b18f9de82cc99a3964eed60a" |
632 | integrity sha512-B4qphdSTp0nLsWcuei07JPKeZej4+Hd22MdnulJXQa1nCcGSBlk8FiqenGERaPZ+PuYhz4Li2Wjc8yfJvHgUMw== | 632 | integrity sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g== |
633 | dependencies: | 633 | dependencies: |
634 | "@babel/compat-data" "^7.13.8" | ||
635 | "@babel/helper-compilation-targets" "^7.13.8" | ||
634 | "@babel/helper-plugin-utils" "^7.13.0" | 636 | "@babel/helper-plugin-utils" "^7.13.0" |
635 | "@babel/plugin-syntax-object-rest-spread" "^7.8.0" | 637 | "@babel/plugin-syntax-object-rest-spread" "^7.8.3" |
636 | "@babel/plugin-transform-parameters" "^7.13.0" | 638 | "@babel/plugin-transform-parameters" "^7.13.0" |
637 | 639 | ||
638 | "@babel/plugin-proposal-optional-catch-binding@^7.12.1": | 640 | "@babel/plugin-proposal-optional-catch-binding@^7.12.1": |
639 | version "7.12.13" | 641 | version "7.13.8" |
640 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.13.tgz#4640520afe57728af14b4d1574ba844f263bcae5" | 642 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz#3ad6bd5901506ea996fc31bdcf3ccfa2bed71107" |
641 | integrity sha512-9+MIm6msl9sHWg58NvqpNpLtuFbmpFYk37x8kgnGzAHvX35E1FyAwSUt5hIkSoWJFSAH+iwU8bJ4fcD1zKXOzg== | 643 | integrity sha512-0wS/4DUF1CuTmGo+NiaHfHcVSeSLj5S3e6RivPTg/2k3wOv3jO35tZ6/ZWsQhQMvdgI7CwphjQa/ccarLymHVA== |
642 | dependencies: | 644 | dependencies: |
643 | "@babel/helper-plugin-utils" "^7.12.13" | 645 | "@babel/helper-plugin-utils" "^7.13.0" |
644 | "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" | 646 | "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" |
645 | 647 | ||
646 | "@babel/plugin-proposal-optional-chaining@^7.12.7": | 648 | "@babel/plugin-proposal-optional-chaining@^7.12.7": |
647 | version "7.13.0" | 649 | version "7.13.12" |
648 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.0.tgz#75b41ce0d883d19e8fe635fc3f846be3b1664f4d" | 650 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz#ba9feb601d422e0adea6760c2bd6bbb7bfec4866" |
649 | integrity sha512-OVRQOZEBP2luZrvEbNSX5FfWDousthhdEoAOpej+Tpe58HFLvqRClT89RauIvBuCDFEip7GW1eT86/5lMy2RNA== | 651 | integrity sha512-fcEdKOkIB7Tf4IxrgEVeFC4zeJSTr78no9wTdBuZZbqF64kzllU0ybo2zrzm7gUQfxGhBgq4E39oRs8Zx/RMYQ== |
650 | dependencies: | 652 | dependencies: |
651 | "@babel/helper-plugin-utils" "^7.13.0" | 653 | "@babel/helper-plugin-utils" "^7.13.0" |
652 | "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" | 654 | "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" |
653 | "@babel/plugin-syntax-optional-chaining" "^7.8.0" | 655 | "@babel/plugin-syntax-optional-chaining" "^7.8.3" |
654 | 656 | ||
655 | "@babel/plugin-proposal-private-methods@^7.12.1": | 657 | "@babel/plugin-proposal-private-methods@^7.12.1": |
656 | version "7.13.0" | 658 | version "7.13.0" |
@@ -668,7 +670,7 @@ | |||
668 | "@babel/helper-create-regexp-features-plugin" "^7.12.13" | 670 | "@babel/helper-create-regexp-features-plugin" "^7.12.13" |
669 | "@babel/helper-plugin-utils" "^7.12.13" | 671 | "@babel/helper-plugin-utils" "^7.12.13" |
670 | 672 | ||
671 | "@babel/plugin-syntax-async-generators@^7.8.0": | 673 | "@babel/plugin-syntax-async-generators@^7.8.0", "@babel/plugin-syntax-async-generators@^7.8.4": |
672 | version "7.8.4" | 674 | version "7.8.4" |
673 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" | 675 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" |
674 | integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== | 676 | integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== |
@@ -682,7 +684,7 @@ | |||
682 | dependencies: | 684 | dependencies: |
683 | "@babel/helper-plugin-utils" "^7.12.13" | 685 | "@babel/helper-plugin-utils" "^7.12.13" |
684 | 686 | ||
685 | "@babel/plugin-syntax-dynamic-import@^7.8.0": | 687 | "@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": |
686 | version "7.8.3" | 688 | version "7.8.3" |
687 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" | 689 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" |
688 | integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== | 690 | integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== |
@@ -696,7 +698,7 @@ | |||
696 | dependencies: | 698 | dependencies: |
697 | "@babel/helper-plugin-utils" "^7.8.3" | 699 | "@babel/helper-plugin-utils" "^7.8.3" |
698 | 700 | ||
699 | "@babel/plugin-syntax-json-strings@^7.8.0": | 701 | "@babel/plugin-syntax-json-strings@^7.8.0", "@babel/plugin-syntax-json-strings@^7.8.3": |
700 | version "7.8.3" | 702 | version "7.8.3" |
701 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" | 703 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" |
702 | integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== | 704 | integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== |
@@ -710,7 +712,7 @@ | |||
710 | dependencies: | 712 | dependencies: |
711 | "@babel/helper-plugin-utils" "^7.10.4" | 713 | "@babel/helper-plugin-utils" "^7.10.4" |
712 | 714 | ||
713 | "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": | 715 | "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": |
714 | version "7.8.3" | 716 | version "7.8.3" |
715 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" | 717 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" |
716 | integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== | 718 | integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== |
@@ -724,21 +726,21 @@ | |||
724 | dependencies: | 726 | dependencies: |
725 | "@babel/helper-plugin-utils" "^7.10.4" | 727 | "@babel/helper-plugin-utils" "^7.10.4" |
726 | 728 | ||
727 | "@babel/plugin-syntax-object-rest-spread@^7.8.0": | 729 | "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": |
728 | version "7.8.3" | 730 | version "7.8.3" |
729 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" | 731 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" |
730 | integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== | 732 | integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== |
731 | dependencies: | 733 | dependencies: |
732 | "@babel/helper-plugin-utils" "^7.8.0" | 734 | "@babel/helper-plugin-utils" "^7.8.0" |
733 | 735 | ||
734 | "@babel/plugin-syntax-optional-catch-binding@^7.8.0": | 736 | "@babel/plugin-syntax-optional-catch-binding@^7.8.0", "@babel/plugin-syntax-optional-catch-binding@^7.8.3": |
735 | version "7.8.3" | 737 | version "7.8.3" |
736 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" | 738 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" |
737 | integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== | 739 | integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== |
738 | dependencies: | 740 | dependencies: |
739 | "@babel/helper-plugin-utils" "^7.8.0" | 741 | "@babel/helper-plugin-utils" "^7.8.0" |
740 | 742 | ||
741 | "@babel/plugin-syntax-optional-chaining@^7.8.0": | 743 | "@babel/plugin-syntax-optional-chaining@^7.8.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": |
742 | version "7.8.3" | 744 | version "7.8.3" |
743 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" | 745 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" |
744 | integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== | 746 | integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== |
@@ -880,9 +882,9 @@ | |||
880 | babel-plugin-dynamic-import-node "^2.3.3" | 882 | babel-plugin-dynamic-import-node "^2.3.3" |
881 | 883 | ||
882 | "@babel/plugin-transform-modules-commonjs@^7.12.1": | 884 | "@babel/plugin-transform-modules-commonjs@^7.12.1": |
883 | version "7.13.0" | 885 | version "7.13.8" |
884 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.0.tgz#276932693a20d12c9776093fdc99c0d9995e34c6" | 886 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.8.tgz#7b01ad7c2dcf2275b06fa1781e00d13d420b3e1b" |
885 | integrity sha512-j7397PkIB4lcn25U2dClK6VLC6pr2s3q+wbE8R3vJvY6U1UTBBj0n6F+5v6+Fd/UwfDPAorMOs2TV+T4M+owpQ== | 887 | integrity sha512-9QiOx4MEGglfYZ4XOnU79OHr6vIWUakIj9b4mioN8eQIoEh+pf5p/zEB36JpDFWA12nNMiRf7bfoRvl9Rn79Bw== |
886 | dependencies: | 888 | dependencies: |
887 | "@babel/helper-module-transforms" "^7.13.0" | 889 | "@babel/helper-module-transforms" "^7.13.0" |
888 | "@babel/helper-plugin-utils" "^7.13.0" | 890 | "@babel/helper-plugin-utils" "^7.13.0" |
@@ -890,13 +892,13 @@ | |||
890 | babel-plugin-dynamic-import-node "^2.3.3" | 892 | babel-plugin-dynamic-import-node "^2.3.3" |
891 | 893 | ||
892 | "@babel/plugin-transform-modules-systemjs@^7.12.1": | 894 | "@babel/plugin-transform-modules-systemjs@^7.12.1": |
893 | version "7.12.13" | 895 | version "7.13.8" |
894 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.13.tgz#351937f392c7f07493fc79b2118201d50404a3c5" | 896 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz#6d066ee2bff3c7b3d60bf28dec169ad993831ae3" |
895 | integrity sha512-aHfVjhZ8QekaNF/5aNdStCGzwTbU7SI5hUybBKlMzqIMC7w7Ho8hx5a4R/DkTHfRfLwHGGxSpFt9BfxKCoXKoA== | 897 | integrity sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A== |
896 | dependencies: | 898 | dependencies: |
897 | "@babel/helper-hoist-variables" "^7.12.13" | 899 | "@babel/helper-hoist-variables" "^7.13.0" |
898 | "@babel/helper-module-transforms" "^7.12.13" | 900 | "@babel/helper-module-transforms" "^7.13.0" |
899 | "@babel/helper-plugin-utils" "^7.12.13" | 901 | "@babel/helper-plugin-utils" "^7.13.0" |
900 | "@babel/helper-validator-identifier" "^7.12.11" | 902 | "@babel/helper-validator-identifier" "^7.12.11" |
901 | babel-plugin-dynamic-import-node "^2.3.3" | 903 | babel-plugin-dynamic-import-node "^2.3.3" |
902 | 904 | ||
@@ -1109,9 +1111,9 @@ | |||
1109 | regenerator-runtime "^0.13.4" | 1111 | regenerator-runtime "^0.13.4" |
1110 | 1112 | ||
1111 | "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": | 1113 | "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": |
1112 | version "7.13.7" | 1114 | version "7.13.10" |
1113 | resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.7.tgz#d494e39d198ee9ca04f4dcb76d25d9d7a1dc961a" | 1115 | resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d" |
1114 | integrity sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA== | 1116 | integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw== |
1115 | dependencies: | 1117 | dependencies: |
1116 | regenerator-runtime "^0.13.4" | 1118 | regenerator-runtime "^0.13.4" |
1117 | 1119 | ||
@@ -1148,16 +1150,16 @@ | |||
1148 | globals "^11.1.0" | 1150 | globals "^11.1.0" |
1149 | lodash "^4.17.19" | 1151 | lodash "^4.17.19" |
1150 | 1152 | ||
1151 | "@babel/types@^7.12.1", "@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.13", "@babel/types@^7.12.7", "@babel/types@^7.13.0", "@babel/types@^7.4.4", "@babel/types@^7.8.3", "@babel/types@^7.8.6": | 1153 | "@babel/types@^7.12.1", "@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.13", "@babel/types@^7.12.7", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.4.4", "@babel/types@^7.8.3", "@babel/types@^7.8.6": |
1152 | version "7.13.0" | 1154 | version "7.13.12" |
1153 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80" | 1155 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.12.tgz#edbf99208ef48852acdff1c8a681a1e4ade580cd" |
1154 | integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA== | 1156 | integrity sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA== |
1155 | dependencies: | 1157 | dependencies: |
1156 | "@babel/helper-validator-identifier" "^7.12.11" | 1158 | "@babel/helper-validator-identifier" "^7.12.11" |
1157 | lodash "^4.17.19" | 1159 | lodash "^4.17.19" |
1158 | to-fast-properties "^2.0.0" | 1160 | to-fast-properties "^2.0.0" |
1159 | 1161 | ||
1160 | "@discoveryjs/json-ext@^0.5.0": | 1162 | "@discoveryjs/json-ext@0.5.2", "@discoveryjs/json-ext@^0.5.0": |
1161 | version "0.5.2" | 1163 | version "0.5.2" |
1162 | resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752" | 1164 | resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752" |
1163 | integrity sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg== | 1165 | integrity sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg== |
@@ -1197,12 +1199,12 @@ | |||
1197 | dependencies: | 1199 | dependencies: |
1198 | tslib "^2.0.0" | 1200 | tslib "^2.0.0" |
1199 | 1201 | ||
1200 | "@ngtools/webpack@11.2.2": | 1202 | "@ngtools/webpack@11.2.5": |
1201 | version "11.2.2" | 1203 | version "11.2.5" |
1202 | resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-11.2.2.tgz#647862ed19761796c7f84d5fb3305661d2a3af67" | 1204 | resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-11.2.5.tgz#3e2265145d19fcdda9ec2894ccded83658b1fa66" |
1203 | integrity sha512-X1M/Xs0kLi9FrOIU6yJ74q3pCzhgwPQowO1XjJ68KLOoMbj/DM6Qm0Hi9N0Ay8h0s7BIdjKEu/C3pCdGu1Q54w== | 1205 | integrity sha512-7fhg8hvqTiTS5ESiEN4xR2qRnOVX0rhVSckMXbAFvNYTwQOuS865RiBrYCJ4CsKhGJ9P7XS5i2EIwA3/aLSivg== |
1204 | dependencies: | 1206 | dependencies: |
1205 | "@angular-devkit/core" "11.2.2" | 1207 | "@angular-devkit/core" "11.2.5" |
1206 | enhanced-resolve "5.7.0" | 1208 | enhanced-resolve "5.7.0" |
1207 | webpack-sources "2.2.0" | 1209 | webpack-sources "2.2.0" |
1208 | 1210 | ||
@@ -1331,15 +1333,14 @@ | |||
1331 | infer-owner "^1.0.4" | 1333 | infer-owner "^1.0.4" |
1332 | 1334 | ||
1333 | "@npmcli/run-script@^1.3.0": | 1335 | "@npmcli/run-script@^1.3.0": |
1334 | version "1.8.3" | 1336 | version "1.8.4" |
1335 | resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.3.tgz#07f440ed492400bb1114369bc37315eeaaae2bb3" | 1337 | resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.4.tgz#03ced92503a6fe948cbc0975ce39210bc5e824d6" |
1336 | integrity sha512-ELPGWAVU/xyU+A+H3pEPj0QOvYwLTX71RArXcClFzeiyJ/b/McsZ+d0QxpznvfFtZzxGN/gz/1cvlqICR4/suQ== | 1338 | integrity sha512-Yd9HXTtF1JGDXZw0+SOn+mWLYS0e7bHBHVC/2C8yqs4wUrs/k8rwBSinD7rfk+3WG/MFGRZKxjyoD34Pch2E/A== |
1337 | dependencies: | 1339 | dependencies: |
1338 | "@npmcli/node-gyp" "^1.0.2" | 1340 | "@npmcli/node-gyp" "^1.0.2" |
1339 | "@npmcli/promise-spawn" "^1.3.2" | 1341 | "@npmcli/promise-spawn" "^1.3.2" |
1340 | infer-owner "^1.0.4" | 1342 | infer-owner "^1.0.4" |
1341 | node-gyp "^7.1.0" | 1343 | node-gyp "^7.1.0" |
1342 | puka "^1.0.1" | ||
1343 | read-package-json-fast "^2.0.1" | 1344 | read-package-json-fast "^2.0.1" |
1344 | 1345 | ||
1345 | "@polka/url@^1.0.0-next.9": | 1346 | "@polka/url@^1.0.0-next.9": |
@@ -1347,22 +1348,22 @@ | |||
1347 | resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.11.tgz#aeb16f50649a91af79dbe36574b66d0f9e4d9f71" | 1348 | resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.11.tgz#aeb16f50649a91af79dbe36574b66d0f9e4d9f71" |
1348 | integrity sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA== | 1349 | integrity sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA== |
1349 | 1350 | ||
1350 | "@schematics/angular@11.2.2": | 1351 | "@schematics/angular@11.2.5": |
1351 | version "11.2.2" | 1352 | version "11.2.5" |
1352 | resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-11.2.2.tgz#ff69a66b6e1acf5aa36ed0795973f3f57d893d0b" | 1353 | resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-11.2.5.tgz#c984687c95be32d3fa6016faa8b5a61715a830f5" |
1353 | integrity sha512-TcxPy58adUnkirGXyZVVSMuKkA0eIz2PWSQWEgB9l7kO+5LvDOn+RMoc6AVx0s/bU9nH+eozBUJ1XAD/E8QnYQ== | 1354 | integrity sha512-pjaK0gZyqhzgAVxMKElG6cDpAvNZ3adVCTA8dhEixpH+JaQdoczl59hMn7rH75yQW0PApe+8g7HMwVK6bLRmxQ== |
1354 | dependencies: | 1355 | dependencies: |
1355 | "@angular-devkit/core" "11.2.2" | 1356 | "@angular-devkit/core" "11.2.5" |
1356 | "@angular-devkit/schematics" "11.2.2" | 1357 | "@angular-devkit/schematics" "11.2.5" |
1357 | jsonc-parser "3.0.0" | 1358 | jsonc-parser "3.0.0" |
1358 | 1359 | ||
1359 | "@schematics/update@0.1102.2": | 1360 | "@schematics/update@0.1102.5": |
1360 | version "0.1102.2" | 1361 | version "0.1102.5" |
1361 | resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1102.2.tgz#f8aed68bbcefdc8633c7804e47ff891ef06bd5ef" | 1362 | resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1102.5.tgz#538493f0a7d06d794d521cca4f2ff588f05cc733" |
1362 | integrity sha512-Nz8kjeixzDnOw00bnZznq3qrbIv8yWEWNb9eDkRBqgOUXQwlhKJY/sYBK58JF2D+conaRVuEqMsBlX08GlFtIA== | 1363 | integrity sha512-iz9pM8mabieqQnPZjrqP5jfRFvPm81/uIg46kY3KjtDtSBi4GAF2dnFyX1dC2mG1rq+e+8zeQLvOvhdLifYlEA== |
1363 | dependencies: | 1364 | dependencies: |
1364 | "@angular-devkit/core" "11.2.2" | 1365 | "@angular-devkit/core" "11.2.5" |
1365 | "@angular-devkit/schematics" "11.2.2" | 1366 | "@angular-devkit/schematics" "11.2.5" |
1366 | "@yarnpkg/lockfile" "1.1.0" | 1367 | "@yarnpkg/lockfile" "1.1.0" |
1367 | ini "2.0.0" | 1368 | ini "2.0.0" |
1368 | npm-package-arg "^8.0.0" | 1369 | npm-package-arg "^8.0.0" |
@@ -1388,9 +1389,9 @@ | |||
1388 | "@types/node" "*" | 1389 | "@types/node" "*" |
1389 | 1390 | ||
1390 | "@types/chart.js@^2.9.16": | 1391 | "@types/chart.js@^2.9.16": |
1391 | version "2.9.30" | 1392 | version "2.9.31" |
1392 | resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.30.tgz#34b99897f4f5ef0f74c8fe4ced70ac52b4d752dd" | 1393 | resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.31.tgz#e8ebc7ed18eb0e5114c69bd46ef8e0037c89d39d" |
1393 | integrity sha512-EgjxUUZFvf6ls3kW2CwyrnSJhgyKxgwrlp/W5G9wqyPEO9iFatO63zAA7L24YqgMxiDjQ+tG7ODU+2yWH91lPg== | 1394 | integrity sha512-hzS6phN/kx3jClk3iYqEHNnYIRSi4RZrIGJ8CDLjgatpHoftCezvC44uqB3o3OUm9ftU1m7sHG8+RLyPTlACrA== |
1394 | dependencies: | 1395 | dependencies: |
1395 | moment "^2.10.2" | 1396 | moment "^2.10.2" |
1396 | 1397 | ||
@@ -1443,9 +1444,9 @@ | |||
1443 | integrity sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA== | 1444 | integrity sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA== |
1444 | 1445 | ||
1445 | "@types/jasmine@*", "@types/jasmine@^3.3.15": | 1446 | "@types/jasmine@*", "@types/jasmine@^3.3.15": |
1446 | version "3.6.4" | 1447 | version "3.6.7" |
1447 | resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.6.4.tgz#22ade1b692d5656f859ef9bc6c62d88632cc27e0" | 1448 | resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.6.7.tgz#e762d3ead78538efb7900ab932d7daf334acb0b4" |
1448 | integrity sha512-CTdMERA4iGNcxeqzD7pavb4WLIFq6bGnx6nIJD+1D4Knx24GE6QBPrWVhO8UlIy7gf7rbIt3ZD7iIzryRD2TgA== | 1449 | integrity sha512-8dtfiykrpe4Ysn6ONj0tOjmpDIh1vWxPk80eutSeWmyaJvAZXZ84219fS4gLrvz05eidhp7BP17WVQBaXHSyXQ== |
1449 | 1450 | ||
1450 | "@types/jasminewd2@^2.0.3": | 1451 | "@types/jasminewd2@^2.0.3": |
1451 | version "2.0.8" | 1452 | version "2.0.8" |
@@ -1465,9 +1466,9 @@ | |||
1465 | integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== | 1466 | integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== |
1466 | 1467 | ||
1467 | "@types/linkify-it@*": | 1468 | "@types/linkify-it@*": |
1468 | version "3.0.0" | 1469 | version "3.0.1" |
1469 | resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.0.tgz#c0ca4c253664492dbf47a646f31cfd483a6bbc95" | 1470 | resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.1.tgz#4d26a9efe3aa2caf829234ec5a39580fc88b6001" |
1470 | integrity sha512-x9OaQQTb1N2hPZ/LWJsqushexDvz7NgzuZxiRmZio44WPuolTZNHDBCrOxCzRVOMwamJRO2dWax5NbygOf1OTQ== | 1471 | integrity sha512-pQv3Sygwxxh6jYQzXaiyWDAHevJqWtqDUv6t11Sa9CPGiXny66II7Pl6PR8QO5OVysD6HYOkHMeBgIjLnk9SkQ== |
1471 | 1472 | ||
1472 | "@types/linkifyjs@^2.1.2": | 1473 | "@types/linkifyjs@^2.1.2": |
1473 | version "2.1.3" | 1474 | version "2.1.3" |
@@ -1519,10 +1520,10 @@ | |||
1519 | resolved "https://registry.yarnpkg.com/@types/mousetrap/-/mousetrap-1.6.3.tgz#3159a01a2b21c9155a3d8f85588885d725dc987d" | 1520 | resolved "https://registry.yarnpkg.com/@types/mousetrap/-/mousetrap-1.6.3.tgz#3159a01a2b21c9155a3d8f85588885d725dc987d" |
1520 | integrity sha512-13gmo3M2qVvjQrWNseqM3+cR6S2Ss3grbR2NZltgMq94wOwqJYQdgn8qzwDshzgXqMlSUtyPZjysImmktu22ew== | 1521 | integrity sha512-13gmo3M2qVvjQrWNseqM3+cR6S2Ss3grbR2NZltgMq94wOwqJYQdgn8qzwDshzgXqMlSUtyPZjysImmktu22ew== |
1521 | 1522 | ||
1522 | "@types/node@*", "@types/node@^14.0.14", "@types/node@^14.14.10": | 1523 | "@types/node@*", "@types/node@>=10.0.0", "@types/node@^14.0.14": |
1523 | version "14.14.31" | 1524 | version "14.14.35" |
1524 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055" | 1525 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.35.tgz#42c953a4e2b18ab931f72477e7012172f4ffa313" |
1525 | integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g== | 1526 | integrity sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag== |
1526 | 1527 | ||
1527 | "@types/parse-json@^4.0.0": | 1528 | "@types/parse-json@^4.0.0": |
1528 | version "4.0.0" | 1529 | version "4.0.0" |
@@ -1561,11 +1562,12 @@ | |||
1561 | integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== | 1562 | integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== |
1562 | 1563 | ||
1563 | "@types/react@*": | 1564 | "@types/react@*": |
1564 | version "17.0.2" | 1565 | version "17.0.3" |
1565 | resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.2.tgz#3de24c4efef902dd9795a49c75f760cbe4f7a5a8" | 1566 | resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.3.tgz#ba6e215368501ac3826951eef2904574c262cc79" |
1566 | integrity sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA== | 1567 | integrity sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg== |
1567 | dependencies: | 1568 | dependencies: |
1568 | "@types/prop-types" "*" | 1569 | "@types/prop-types" "*" |
1570 | "@types/scheduler" "*" | ||
1569 | csstype "^3.0.2" | 1571 | csstype "^3.0.2" |
1570 | 1572 | ||
1571 | "@types/sanitize-html@1.27.1": | 1573 | "@types/sanitize-html@1.27.1": |
@@ -1575,6 +1577,11 @@ | |||
1575 | dependencies: | 1577 | dependencies: |
1576 | htmlparser2 "^4.1.0" | 1578 | htmlparser2 "^4.1.0" |
1577 | 1579 | ||
1580 | "@types/scheduler@*": | ||
1581 | version "0.16.1" | ||
1582 | resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275" | ||
1583 | integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA== | ||
1584 | |||
1578 | "@types/selenium-webdriver@^3.0.0": | 1585 | "@types/selenium-webdriver@^3.0.0": |
1579 | version "3.0.17" | 1586 | version "3.0.17" |
1580 | resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz#50bea0c3c2acc31c959c5b1e747798b3b3d06d4b" | 1587 | resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz#50bea0c3c2acc31c959c5b1e747798b3b3d06d4b" |
@@ -1605,9 +1612,9 @@ | |||
1605 | integrity sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA== | 1612 | integrity sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA== |
1606 | 1613 | ||
1607 | "@types/uglify-js@*": | 1614 | "@types/uglify-js@*": |
1608 | version "3.12.0" | 1615 | version "3.13.0" |
1609 | resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.12.0.tgz#2bb061c269441620d46b946350c8f16d52ef37c5" | 1616 | resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.13.0.tgz#1cad8df1fb0b143c5aba08de5712ea9d1ff71124" |
1610 | integrity sha512-sYAF+CF9XZ5cvEBkI7RtrG9g2GtMBkviTnBxYYyq+8BWvO4QtXfwwR6a2LFwCi4evMKZfpv6U43ViYvv17Wz3Q== | 1617 | integrity sha512-EGkrJD5Uy+Pg0NUR8uA4bJ5WMfljyad0G+784vLCNUkD+QwOJXUbBYExXfVGf7YtyzdQp3L/XMYcliB987kL5Q== |
1611 | dependencies: | 1618 | dependencies: |
1612 | source-map "^0.6.1" | 1619 | source-map "^0.6.1" |
1613 | 1620 | ||
@@ -1925,9 +1932,9 @@ acorn@^6.4.1: | |||
1925 | integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== | 1932 | integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== |
1926 | 1933 | ||
1927 | acorn@^8.0.4: | 1934 | acorn@^8.0.4: |
1928 | version "8.0.5" | 1935 | version "8.1.0" |
1929 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.0.5.tgz#a3bfb872a74a6a7f661bc81b9849d9cac12601b7" | 1936 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe" |
1930 | integrity sha512-v+DieK/HJkJOpFBETDJioequtc3PfxsWMaxIdIwujtF7FEV/MAyDQLlm6/zPvr7Mix07mLh6ccVwIsloceodlg== | 1937 | integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA== |
1931 | 1938 | ||
1932 | addr-to-ip-port@^1.0.1, addr-to-ip-port@^1.5.1: | 1939 | addr-to-ip-port@^1.0.1, addr-to-ip-port@^1.5.1: |
1933 | version "1.5.1" | 1940 | version "1.5.1" |
@@ -2793,7 +2800,7 @@ bytes@3.1.0: | |||
2793 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" | 2800 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" |
2794 | integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== | 2801 | integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== |
2795 | 2802 | ||
2796 | cacache@15.0.5, cacache@^15.0.5: | 2803 | cacache@15.0.5: |
2797 | version "15.0.5" | 2804 | version "15.0.5" |
2798 | resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" | 2805 | resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" |
2799 | integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== | 2806 | integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== |
@@ -2837,6 +2844,29 @@ cacache@^12.0.2: | |||
2837 | unique-filename "^1.1.1" | 2844 | unique-filename "^1.1.1" |
2838 | y18n "^4.0.0" | 2845 | y18n "^4.0.0" |
2839 | 2846 | ||
2847 | cacache@^15.0.5: | ||
2848 | version "15.0.6" | ||
2849 | resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.6.tgz#65a8c580fda15b59150fb76bf3f3a8e45d583099" | ||
2850 | integrity sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w== | ||
2851 | dependencies: | ||
2852 | "@npmcli/move-file" "^1.0.1" | ||
2853 | chownr "^2.0.0" | ||
2854 | fs-minipass "^2.0.0" | ||
2855 | glob "^7.1.4" | ||
2856 | infer-owner "^1.0.4" | ||
2857 | lru-cache "^6.0.0" | ||
2858 | minipass "^3.1.1" | ||
2859 | minipass-collect "^1.0.2" | ||
2860 | minipass-flush "^1.0.5" | ||
2861 | minipass-pipeline "^1.2.2" | ||
2862 | mkdirp "^1.0.3" | ||
2863 | p-map "^4.0.0" | ||
2864 | promise-inflight "^1.0.1" | ||
2865 | rimraf "^3.0.2" | ||
2866 | ssri "^8.0.1" | ||
2867 | tar "^6.0.2" | ||
2868 | unique-filename "^1.1.1" | ||
2869 | |||
2840 | cache-base@^1.0.1: | 2870 | cache-base@^1.0.1: |
2841 | version "1.0.1" | 2871 | version "1.0.1" |
2842 | resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" | 2872 | resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" |
@@ -2937,9 +2967,9 @@ caniuse-api@^3.0.0: | |||
2937 | lodash.uniq "^4.5.0" | 2967 | lodash.uniq "^4.5.0" |
2938 | 2968 | ||
2939 | caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001181: | 2969 | caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001181: |
2940 | version "1.0.30001192" | 2970 | version "1.0.30001204" |
2941 | resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001192.tgz#b848ebc0ab230cf313d194a4775a30155d50ae40" | 2971 | resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001204.tgz#256c85709a348ec4d175e847a3b515c66e79f2aa" |
2942 | integrity sha512-63OrUnwJj5T1rUmoyqYTdRWBqFFxZFlyZnRRjDR8NSUQFB6A+j/uBORU/SyJ5WzDLg4SPiZH40hQCBNdZ/jmAw== | 2972 | integrity sha512-JUdjWpcxfJ9IPamy2f5JaRDCaqJOxDzOSKtbdx4rH9VivMd1vIzoPumsJa9LoMIi4Fx2BV2KZOxWhNkBjaYivQ== |
2943 | 2973 | ||
2944 | canonical-path@1.0.0: | 2974 | canonical-path@1.0.0: |
2945 | version "1.0.0" | 2975 | version "1.0.0" |
@@ -2971,7 +3001,7 @@ chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: | |||
2971 | escape-string-regexp "^1.0.5" | 3001 | escape-string-regexp "^1.0.5" |
2972 | supports-color "^5.3.0" | 3002 | supports-color "^5.3.0" |
2973 | 3003 | ||
2974 | chalk@^4.0.0, chalk@^4.1.0: | 3004 | chalk@^4.1.0: |
2975 | version "4.1.0" | 3005 | version "4.1.0" |
2976 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" | 3006 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" |
2977 | integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== | 3007 | integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== |
@@ -3081,9 +3111,9 @@ chrome-trace-event@^1.0.2: | |||
3081 | tslib "^1.9.0" | 3111 | tslib "^1.9.0" |
3082 | 3112 | ||
3083 | chunk-store-stream@^4.1.1: | 3113 | chunk-store-stream@^4.1.1: |
3084 | version "4.2.0" | 3114 | version "4.3.0" |
3085 | resolved "https://registry.yarnpkg.com/chunk-store-stream/-/chunk-store-stream-4.2.0.tgz#18f673c495946c4cdcf14124a3ebd5f31eb0ea35" | 3115 | resolved "https://registry.yarnpkg.com/chunk-store-stream/-/chunk-store-stream-4.3.0.tgz#3de5f4dfe19729366c29bb7ed52d139f9af29f0e" |
3086 | integrity sha512-90iueoPoqT2isnmy1fyqwzgFy5FokuaxQuijOQG1VgC/6DaXRfeYN0da8iWENkzqElWhqLxo8pWc7pH9dmxlcA== | 3116 | integrity sha512-qby+/RXoiMoTVtPiylWZt7KFF1jy6M829TzMi2hxZtBIH9ptV19wxcft6zGiXLokJgCbuZPGNGab6DWHqiSEKw== |
3087 | dependencies: | 3117 | dependencies: |
3088 | block-stream2 "^2.0.0" | 3118 | block-stream2 "^2.0.0" |
3089 | readable-stream "^3.6.0" | 3119 | readable-stream "^3.6.0" |
@@ -3143,9 +3173,9 @@ cli-cursor@^3.1.0: | |||
3143 | restore-cursor "^3.1.0" | 3173 | restore-cursor "^3.1.0" |
3144 | 3174 | ||
3145 | cli-spinners@^2.5.0: | 3175 | cli-spinners@^2.5.0: |
3146 | version "2.5.0" | 3176 | version "2.6.0" |
3147 | resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.5.0.tgz#12763e47251bf951cb75c201dfa58ff1bcb2d047" | 3177 | resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" |
3148 | integrity sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ== | 3178 | integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== |
3149 | 3179 | ||
3150 | cli-width@^2.0.0: | 3180 | cli-width@^2.0.0: |
3151 | version "2.2.1" | 3181 | version "2.2.1" |
@@ -3279,9 +3309,9 @@ color-name@^1.0.0, color-name@~1.1.4: | |||
3279 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== | 3309 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== |
3280 | 3310 | ||
3281 | color-string@^1.5.4: | 3311 | color-string@^1.5.4: |
3282 | version "1.5.4" | 3312 | version "1.5.5" |
3283 | resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6" | 3313 | resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014" |
3284 | integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw== | 3314 | integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg== |
3285 | dependencies: | 3315 | dependencies: |
3286 | color-name "^1.0.0" | 3316 | color-name "^1.0.0" |
3287 | simple-swizzle "^0.2.2" | 3317 | simple-swizzle "^0.2.2" |
@@ -3294,10 +3324,10 @@ color@^3.0.0: | |||
3294 | color-convert "^1.9.1" | 3324 | color-convert "^1.9.1" |
3295 | color-string "^1.5.4" | 3325 | color-string "^1.5.4" |
3296 | 3326 | ||
3297 | colorette@^1.2.1: | 3327 | colorette@^1.2.1, colorette@^1.2.2: |
3298 | version "1.2.1" | 3328 | version "1.2.2" |
3299 | resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" | 3329 | resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" |
3300 | integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== | 3330 | integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== |
3301 | 3331 | ||
3302 | colors@1.4.0, colors@^1.4.0: | 3332 | colors@1.4.0, colors@^1.4.0: |
3303 | version "1.4.0" | 3333 | version "1.4.0" |
@@ -3327,9 +3357,9 @@ commander@^6.2.0: | |||
3327 | integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== | 3357 | integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== |
3328 | 3358 | ||
3329 | commander@^7.0.0: | 3359 | commander@^7.0.0: |
3330 | version "7.1.0" | 3360 | version "7.2.0" |
3331 | resolved "https://registry.yarnpkg.com/commander/-/commander-7.1.0.tgz#f2eaecf131f10e36e07d894698226e36ae0eb5ff" | 3361 | resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" |
3332 | integrity sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg== | 3362 | integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== |
3333 | 3363 | ||
3334 | commondir@^1.0.1: | 3364 | commondir@^1.0.1: |
3335 | version "1.0.1" | 3365 | version "1.0.1" |
@@ -3501,9 +3531,9 @@ copy-webpack-plugin@6.3.2: | |||
3501 | webpack-sources "^1.4.3" | 3531 | webpack-sources "^1.4.3" |
3502 | 3532 | ||
3503 | core-js-compat@^3.8.0: | 3533 | core-js-compat@^3.8.0: |
3504 | version "3.9.0" | 3534 | version "3.9.1" |
3505 | resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.9.0.tgz#29da39385f16b71e1915565aa0385c4e0963ad56" | 3535 | resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.9.1.tgz#4e572acfe90aff69d76d8c37759d21a5c59bb455" |
3506 | integrity sha512-YK6fwFjCOKWwGnjFUR3c544YsnA/7DoLL0ysncuOJ4pwbriAtOpvM2bygdlcXbvQCQZ7bBU9CL4t7tGl7ETRpQ== | 3536 | integrity sha512-jXAirMQxrkbiiLsCx9bQPJFA6llDadKMpYrBJQJ3/c4/vsPP/fAf29h24tviRlvwUL6AmY5CHLu2GvjuYviQqA== |
3507 | dependencies: | 3537 | dependencies: |
3508 | browserslist "^4.16.3" | 3538 | browserslist "^4.16.3" |
3509 | semver "7.0.0" | 3539 | semver "7.0.0" |
@@ -3514,9 +3544,9 @@ core-js@3.8.3: | |||
3514 | integrity sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q== | 3544 | integrity sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q== |
3515 | 3545 | ||
3516 | core-js@^3.1.4: | 3546 | core-js@^3.1.4: |
3517 | version "3.9.0" | 3547 | version "3.9.1" |
3518 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.9.0.tgz#790b1bb11553a2272b36e2625c7179db345492f8" | 3548 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.9.1.tgz#cec8de593db8eb2a85ffb0dbdeb312cb6e5460ae" |
3519 | integrity sha512-PyFBJaLq93FlyYdsndE5VaueA9K5cNB7CGzeCj191YYLhkQM0gdZR2SKihM70oF0wdqKSKClv/tEBOpoRmdOVQ== | 3549 | integrity sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg== |
3520 | 3550 | ||
3521 | core-util-is@1.0.2, core-util-is@~1.0.0: | 3551 | core-util-is@1.0.2, core-util-is@~1.0.0: |
3522 | version "1.0.2" | 3552 | version "1.0.2" |
@@ -3691,15 +3721,15 @@ css-loader@5.0.1: | |||
3691 | semver "^7.3.2" | 3721 | semver "^7.3.2" |
3692 | 3722 | ||
3693 | css-loader@^5.0.1: | 3723 | css-loader@^5.0.1: |
3694 | version "5.0.2" | 3724 | version "5.1.3" |
3695 | resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.0.2.tgz#24f758dae349bad0a440c50d7e2067742e0899cb" | 3725 | resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.1.3.tgz#87f6fc96816b20debe3cf682f85c7e56a963d0d1" |
3696 | integrity sha512-gbkBigdcHbmNvZ1Cg6aV6qh6k9N6XOr8YWzISLQGrwk2mgOH8LLrizhkxbDhQtaLtktyKHD4970S0xwz5btfTA== | 3726 | integrity sha512-CoPZvyh8sLiGARK3gqczpfdedbM74klGWurF2CsNZ2lhNaXdLIUks+3Mfax3WBeRuHoglU+m7KG/+7gY6G4aag== |
3697 | dependencies: | 3727 | dependencies: |
3698 | camelcase "^6.2.0" | 3728 | camelcase "^6.2.0" |
3699 | cssesc "^3.0.0" | 3729 | cssesc "^3.0.0" |
3700 | icss-utils "^5.1.0" | 3730 | icss-utils "^5.1.0" |
3701 | loader-utils "^2.0.0" | 3731 | loader-utils "^2.0.0" |
3702 | postcss "^8.2.4" | 3732 | postcss "^8.2.8" |
3703 | postcss-modules-extract-imports "^3.0.0" | 3733 | postcss-modules-extract-imports "^3.0.0" |
3704 | postcss-modules-local-by-default "^4.0.0" | 3734 | postcss-modules-local-by-default "^4.0.0" |
3705 | postcss-modules-scope "^3.0.0" | 3735 | postcss-modules-scope "^3.0.0" |
@@ -4081,9 +4111,9 @@ destroy@~1.0.4: | |||
4081 | integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= | 4111 | integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= |
4082 | 4112 | ||
4083 | detect-node@^2.0.4: | 4113 | detect-node@^2.0.4: |
4084 | version "2.0.4" | 4114 | version "2.0.5" |
4085 | resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" | 4115 | resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.5.tgz#9d270aa7eaa5af0b72c4c9d9b814e7f4ce738b79" |
4086 | integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== | 4116 | integrity sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw== |
4087 | 4117 | ||
4088 | dexie@^3.0.0: | 4118 | dexie@^3.0.0: |
4089 | version "3.0.3" | 4119 | version "3.0.3" |
@@ -4241,9 +4271,9 @@ domutils@^1.5.1, domutils@^1.7.0: | |||
4241 | domelementtype "1" | 4271 | domelementtype "1" |
4242 | 4272 | ||
4243 | domutils@^2.0.0, domutils@^2.4.4: | 4273 | domutils@^2.0.0, domutils@^2.4.4: |
4244 | version "2.4.4" | 4274 | version "2.5.0" |
4245 | resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3" | 4275 | resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.5.0.tgz#42f49cffdabb92ad243278b331fd761c1c2d3039" |
4246 | integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA== | 4276 | integrity sha512-Ho16rzNMOFk2fPwChGh3D2D9OEHAfG19HgmRR2l+WLSsIstNsAYBzePH412bL0y5T44ejABIVfTHQ8nqi/tBCg== |
4247 | dependencies: | 4277 | dependencies: |
4248 | dom-serializer "^1.0.1" | 4278 | dom-serializer "^1.0.1" |
4249 | domelementtype "^2.0.1" | 4279 | domelementtype "^2.0.1" |
@@ -4293,9 +4323,9 @@ ee-first@1.1.1: | |||
4293 | integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= | 4323 | integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= |
4294 | 4324 | ||
4295 | electron-to-chromium@^1.3.649: | 4325 | electron-to-chromium@^1.3.649: |
4296 | version "1.3.673" | 4326 | version "1.3.695" |
4297 | resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.673.tgz#b4f81c930b388f962b7eba20d0483299aaa40913" | 4327 | resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.695.tgz#955f419cf99137226180cc4cca2e59015a4e248d" |
4298 | integrity sha512-ms+QR2ckfrrpEAjXweLx6kNCbpAl66DcW//3BZD4BV5KhUgr0RZRce1ON/9J3QyA3JO28nzgb5Xv8DnPr05ILg== | 4328 | integrity sha512-lz66RliUqLHU1Ojxx1A4QUxKydjiQ79Y4dZyPobs2Dmxj5aVL2TM3KoQ2Gs7HS703Bfny+ukI3KOxwAB0xceHQ== |
4299 | 4329 | ||
4300 | elliptic@^6.5.3: | 4330 | elliptic@^6.5.3: |
4301 | version "6.5.4" | 4331 | version "6.5.4" |
@@ -4357,9 +4387,9 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: | |||
4357 | once "^1.4.0" | 4387 | once "^1.4.0" |
4358 | 4388 | ||
4359 | engine.io-client@~4.1.0: | 4389 | engine.io-client@~4.1.0: |
4360 | version "4.1.1" | 4390 | version "4.1.2" |
4361 | resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-4.1.1.tgz#109942705079f15a4fcf1090bc86d3a1341c0a61" | 4391 | resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-4.1.2.tgz#823b4f005360321c41445fc23ce8ee028ef2e36b" |
4362 | integrity sha512-iYasV/EttP/2pLrdowe9G3zwlNIFhwny8VSIh+vPlMnYZqSzLsTzSLa9hFy015OrH1s4fzoYxeHjVkO8hSFKwg== | 4392 | integrity sha512-1mwvwKYMa0AaCy+sPgvJ/SnKyO5MJZ1HEeXfA3Rm/KHkHGiYD5bQVq8QzvIrkI01FuVtOdZC5lWdRw1BGXB2NQ== |
4363 | dependencies: | 4393 | dependencies: |
4364 | base64-arraybuffer "0.1.4" | 4394 | base64-arraybuffer "0.1.4" |
4365 | component-emitter "~1.3.0" | 4395 | component-emitter "~1.3.0" |
@@ -4437,9 +4467,9 @@ entities@~2.1.0: | |||
4437 | integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== | 4467 | integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== |
4438 | 4468 | ||
4439 | env-paths@^2.2.0: | 4469 | env-paths@^2.2.0: |
4440 | version "2.2.0" | 4470 | version "2.2.1" |
4441 | resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" | 4471 | resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" |
4442 | integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== | 4472 | integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== |
4443 | 4473 | ||
4444 | envinfo@^7.7.3: | 4474 | envinfo@^7.7.3: |
4445 | version "7.7.4" | 4475 | version "7.7.4" |
@@ -4470,42 +4500,27 @@ error-ex@^1.2.0, error-ex@^1.3.1: | |||
4470 | dependencies: | 4500 | dependencies: |
4471 | is-arrayish "^0.2.1" | 4501 | is-arrayish "^0.2.1" |
4472 | 4502 | ||
4473 | es-abstract@^1.17.2: | 4503 | es-abstract@^1.17.2, es-abstract@^1.18.0-next.2: |
4474 | version "1.17.7" | 4504 | version "1.18.0" |
4475 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c" | 4505 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" |
4476 | integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g== | 4506 | integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== |
4477 | dependencies: | ||
4478 | es-to-primitive "^1.2.1" | ||
4479 | function-bind "^1.1.1" | ||
4480 | has "^1.0.3" | ||
4481 | has-symbols "^1.0.1" | ||
4482 | is-callable "^1.2.2" | ||
4483 | is-regex "^1.1.1" | ||
4484 | object-inspect "^1.8.0" | ||
4485 | object-keys "^1.1.1" | ||
4486 | object.assign "^4.1.1" | ||
4487 | string.prototype.trimend "^1.0.1" | ||
4488 | string.prototype.trimstart "^1.0.1" | ||
4489 | |||
4490 | es-abstract@^1.18.0-next.2: | ||
4491 | version "1.18.0-next.2" | ||
4492 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.2.tgz#088101a55f0541f595e7e057199e27ddc8f3a5c2" | ||
4493 | integrity sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw== | ||
4494 | dependencies: | 4507 | dependencies: |
4495 | call-bind "^1.0.2" | 4508 | call-bind "^1.0.2" |
4496 | es-to-primitive "^1.2.1" | 4509 | es-to-primitive "^1.2.1" |
4497 | function-bind "^1.1.1" | 4510 | function-bind "^1.1.1" |
4498 | get-intrinsic "^1.0.2" | 4511 | get-intrinsic "^1.1.1" |
4499 | has "^1.0.3" | 4512 | has "^1.0.3" |
4500 | has-symbols "^1.0.1" | 4513 | has-symbols "^1.0.2" |
4501 | is-callable "^1.2.2" | 4514 | is-callable "^1.2.3" |
4502 | is-negative-zero "^2.0.1" | 4515 | is-negative-zero "^2.0.1" |
4503 | is-regex "^1.1.1" | 4516 | is-regex "^1.1.2" |
4517 | is-string "^1.0.5" | ||
4504 | object-inspect "^1.9.0" | 4518 | object-inspect "^1.9.0" |
4505 | object-keys "^1.1.1" | 4519 | object-keys "^1.1.1" |
4506 | object.assign "^4.1.2" | 4520 | object.assign "^4.1.2" |
4507 | string.prototype.trimend "^1.0.3" | 4521 | string.prototype.trimend "^1.0.4" |
4508 | string.prototype.trimstart "^1.0.3" | 4522 | string.prototype.trimstart "^1.0.4" |
4523 | unbox-primitive "^1.0.0" | ||
4509 | 4524 | ||
4510 | es-to-primitive@^1.2.1: | 4525 | es-to-primitive@^1.2.1: |
4511 | version "1.2.1" | 4526 | version "1.2.1" |
@@ -4731,14 +4746,14 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.3: | |||
4731 | integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== | 4746 | integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== |
4732 | 4747 | ||
4733 | events@^3.0.0: | 4748 | events@^3.0.0: |
4734 | version "3.2.0" | 4749 | version "3.3.0" |
4735 | resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" | 4750 | resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" |
4736 | integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== | 4751 | integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== |
4737 | 4752 | ||
4738 | eventsource@^1.0.7: | 4753 | eventsource@^1.0.7: |
4739 | version "1.0.7" | 4754 | version "1.1.0" |
4740 | resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" | 4755 | resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.0.tgz#00e8ca7c92109e94b0ddf32dac677d841028cfaf" |
4741 | integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== | 4756 | integrity sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg== |
4742 | dependencies: | 4757 | dependencies: |
4743 | original "^1.0.0" | 4758 | original "^1.0.0" |
4744 | 4759 | ||
@@ -5109,9 +5124,9 @@ focus-visible@^5.0.2: | |||
5109 | integrity sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ== | 5124 | integrity sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ== |
5110 | 5125 | ||
5111 | follow-redirects@^1.0.0: | 5126 | follow-redirects@^1.0.0: |
5112 | version "1.13.2" | 5127 | version "1.13.3" |
5113 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.2.tgz#dd73c8effc12728ba5cf4259d760ea5fb83e3147" | 5128 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" |
5114 | integrity sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA== | 5129 | integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA== |
5115 | 5130 | ||
5116 | for-in@^1.0.2: | 5131 | for-in@^1.0.2: |
5117 | version "1.0.2" | 5132 | version "1.0.2" |
@@ -5301,7 +5316,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: | |||
5301 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" | 5316 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" |
5302 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== | 5317 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== |
5303 | 5318 | ||
5304 | get-intrinsic@^1.0.2: | 5319 | get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: |
5305 | version "1.1.1" | 5320 | version "1.1.1" |
5306 | resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" | 5321 | resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" |
5307 | integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== | 5322 | integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== |
@@ -5353,9 +5368,9 @@ glob-parent@^3.1.0: | |||
5353 | path-dirname "^1.0.0" | 5368 | path-dirname "^1.0.0" |
5354 | 5369 | ||
5355 | glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@~5.1.0: | 5370 | glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@~5.1.0: |
5356 | version "5.1.1" | 5371 | version "5.1.2" |
5357 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" | 5372 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" |
5358 | integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== | 5373 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== |
5359 | dependencies: | 5374 | dependencies: |
5360 | is-glob "^4.0.1" | 5375 | is-glob "^4.0.1" |
5361 | 5376 | ||
@@ -5410,9 +5425,9 @@ globals@^9.2.0: | |||
5410 | integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== | 5425 | integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== |
5411 | 5426 | ||
5412 | globby@^11.0.1: | 5427 | globby@^11.0.1: |
5413 | version "11.0.2" | 5428 | version "11.0.3" |
5414 | resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.2.tgz#1af538b766a3b540ebfb58a32b2e2d5897321d83" | 5429 | resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" |
5415 | integrity sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og== | 5430 | integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== |
5416 | dependencies: | 5431 | dependencies: |
5417 | array-union "^2.1.0" | 5432 | array-union "^2.1.0" |
5418 | dir-glob "^3.0.1" | 5433 | dir-glob "^3.0.1" |
@@ -5497,6 +5512,11 @@ has-ansi@^2.0.0: | |||
5497 | dependencies: | 5512 | dependencies: |
5498 | ansi-regex "^2.0.0" | 5513 | ansi-regex "^2.0.0" |
5499 | 5514 | ||
5515 | has-bigints@^1.0.0: | ||
5516 | version "1.0.1" | ||
5517 | resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" | ||
5518 | integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== | ||
5519 | |||
5500 | has-cors@1.1.0: | 5520 | has-cors@1.1.0: |
5501 | version "1.1.0" | 5521 | version "1.1.0" |
5502 | resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" | 5522 | resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" |
@@ -5512,10 +5532,10 @@ has-flag@^4.0.0: | |||
5512 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" | 5532 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" |
5513 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== | 5533 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== |
5514 | 5534 | ||
5515 | has-symbols@^1.0.1: | 5535 | has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2: |
5516 | version "1.0.1" | 5536 | version "1.0.2" |
5517 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" | 5537 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" |
5518 | integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== | 5538 | integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== |
5519 | 5539 | ||
5520 | has-unicode@^2.0.0: | 5540 | has-unicode@^2.0.0: |
5521 | version "2.0.1" | 5541 | version "2.0.1" |
@@ -5616,6 +5636,13 @@ hosted-git-info@^3.0.6: | |||
5616 | dependencies: | 5636 | dependencies: |
5617 | lru-cache "^6.0.0" | 5637 | lru-cache "^6.0.0" |
5618 | 5638 | ||
5639 | hosted-git-info@^4.0.1: | ||
5640 | version "4.0.1" | ||
5641 | resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.1.tgz#710ef5452ea429a844abc33c981056e7371edab7" | ||
5642 | integrity sha512-eT7NrxAsppPRQEBSwKSosReE+v8OzABwEScQYk5d4uxaEPlzxTIku7LINXtBGalthkLhJnq5lBI89PfK43zAKg== | ||
5643 | dependencies: | ||
5644 | lru-cache "^6.0.0" | ||
5645 | |||
5619 | hpack.js@^2.1.6: | 5646 | hpack.js@^2.1.6: |
5620 | version "2.1.6" | 5647 | version "2.1.6" |
5621 | resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" | 5648 | resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" |
@@ -5712,9 +5739,9 @@ htmlparser2@^4.1.0: | |||
5712 | entities "^2.0.0" | 5739 | entities "^2.0.0" |
5713 | 5740 | ||
5714 | htmlparser2@^6.0.0: | 5741 | htmlparser2@^6.0.0: |
5715 | version "6.0.0" | 5742 | version "6.0.1" |
5716 | resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.0.tgz#c2da005030390908ca4c91e5629e418e0665ac01" | 5743 | resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.1.tgz#422521231ef6d42e56bd411da8ba40aa36e91446" |
5717 | integrity sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw== | 5744 | integrity sha512-GDKPd+vk4jvSuvCbyuzx/unmXkk090Azec7LovXP8as1Hn8q9p3hbjmDGbUqqhknw0ajwit6LiiWqfiTUPMK7w== |
5718 | dependencies: | 5745 | dependencies: |
5719 | domelementtype "^2.0.1" | 5746 | domelementtype "^2.0.1" |
5720 | domhandler "^4.0.0" | 5747 | domhandler "^4.0.0" |
@@ -6133,6 +6160,11 @@ is-ascii@^1.0.0: | |||
6133 | resolved "https://registry.yarnpkg.com/is-ascii/-/is-ascii-1.0.0.tgz#f02ad0259a0921cd199ff21ce1b09e0f6b4e3929" | 6160 | resolved "https://registry.yarnpkg.com/is-ascii/-/is-ascii-1.0.0.tgz#f02ad0259a0921cd199ff21ce1b09e0f6b4e3929" |
6134 | integrity sha1-8CrQJZoJIc0Zn/Ic4bCeD2tOOSk= | 6161 | integrity sha1-8CrQJZoJIc0Zn/Ic4bCeD2tOOSk= |
6135 | 6162 | ||
6163 | is-bigint@^1.0.1: | ||
6164 | version "1.0.1" | ||
6165 | resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" | ||
6166 | integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== | ||
6167 | |||
6136 | is-binary-path@^1.0.0: | 6168 | is-binary-path@^1.0.0: |
6137 | version "1.0.1" | 6169 | version "1.0.1" |
6138 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" | 6170 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" |
@@ -6147,12 +6179,19 @@ is-binary-path@~2.1.0: | |||
6147 | dependencies: | 6179 | dependencies: |
6148 | binary-extensions "^2.0.0" | 6180 | binary-extensions "^2.0.0" |
6149 | 6181 | ||
6182 | is-boolean-object@^1.1.0: | ||
6183 | version "1.1.0" | ||
6184 | resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0" | ||
6185 | integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== | ||
6186 | dependencies: | ||
6187 | call-bind "^1.0.0" | ||
6188 | |||
6150 | is-buffer@^1.1.5: | 6189 | is-buffer@^1.1.5: |
6151 | version "1.1.6" | 6190 | version "1.1.6" |
6152 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" | 6191 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" |
6153 | integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== | 6192 | integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== |
6154 | 6193 | ||
6155 | is-callable@^1.1.4, is-callable@^1.2.2: | 6194 | is-callable@^1.1.4, is-callable@^1.2.3: |
6156 | version "1.2.3" | 6195 | version "1.2.3" |
6157 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" | 6196 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" |
6158 | integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== | 6197 | integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== |
@@ -6312,6 +6351,11 @@ is-negative-zero@^2.0.1: | |||
6312 | resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" | 6351 | resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" |
6313 | integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== | 6352 | integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== |
6314 | 6353 | ||
6354 | is-number-object@^1.0.4: | ||
6355 | version "1.0.4" | ||
6356 | resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" | ||
6357 | integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== | ||
6358 | |||
6315 | is-number@^3.0.0: | 6359 | is-number@^3.0.0: |
6316 | version "3.0.0" | 6360 | version "3.0.0" |
6317 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" | 6361 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" |
@@ -6384,7 +6428,7 @@ is-property@^1.0.0, is-property@^1.0.2: | |||
6384 | resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" | 6428 | resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" |
6385 | integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= | 6429 | integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= |
6386 | 6430 | ||
6387 | is-regex@^1.0.4, is-regex@^1.1.1: | 6431 | is-regex@^1.0.4, is-regex@^1.1.2: |
6388 | version "1.1.2" | 6432 | version "1.1.2" |
6389 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" | 6433 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" |
6390 | integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== | 6434 | integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== |
@@ -6407,6 +6451,11 @@ is-stream@^2.0.0: | |||
6407 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" | 6451 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" |
6408 | integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== | 6452 | integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== |
6409 | 6453 | ||
6454 | is-string@^1.0.5: | ||
6455 | version "1.0.5" | ||
6456 | resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" | ||
6457 | integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== | ||
6458 | |||
6410 | is-svg@^3.0.0: | 6459 | is-svg@^3.0.0: |
6411 | version "3.0.0" | 6460 | version "3.0.0" |
6412 | resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" | 6461 | resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" |
@@ -6414,7 +6463,7 @@ is-svg@^3.0.0: | |||
6414 | dependencies: | 6463 | dependencies: |
6415 | html-comment-regex "^1.1.0" | 6464 | html-comment-regex "^1.1.0" |
6416 | 6465 | ||
6417 | is-symbol@^1.0.2: | 6466 | is-symbol@^1.0.2, is-symbol@^1.0.3: |
6418 | version "1.0.3" | 6467 | version "1.0.3" |
6419 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" | 6468 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" |
6420 | integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== | 6469 | integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== |
@@ -6426,6 +6475,11 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: | |||
6426 | resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" | 6475 | resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" |
6427 | integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= | 6476 | integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= |
6428 | 6477 | ||
6478 | is-unicode-supported@^0.1.0: | ||
6479 | version "0.1.0" | ||
6480 | resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" | ||
6481 | integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== | ||
6482 | |||
6429 | is-what@^3.12.0: | 6483 | is-what@^3.12.0: |
6430 | version "3.14.1" | 6484 | version "3.14.1" |
6431 | resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1" | 6485 | resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1" |
@@ -6538,16 +6592,21 @@ istanbul-reports@^3.0.2: | |||
6538 | html-escaper "^2.0.0" | 6592 | html-escaper "^2.0.0" |
6539 | istanbul-lib-report "^3.0.0" | 6593 | istanbul-lib-report "^3.0.0" |
6540 | 6594 | ||
6541 | jasmine-core@^3.6.0, jasmine-core@~3.6.0: | 6595 | jasmine-core@^3.6.0: |
6542 | version "3.6.0" | 6596 | version "3.7.1" |
6543 | resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.6.0.tgz#491f3bb23941799c353ceb7a45b38a950ebc5a20" | 6597 | resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.7.1.tgz#0401327f6249eac993d47bbfa18d4e8efacfb561" |
6544 | integrity sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw== | 6598 | integrity sha512-DH3oYDS/AUvvr22+xUBW62m1Xoy7tUlY1tsxKEJvl5JeJ7q8zd1K5bUwiOxdH+erj6l2vAMM3hV25Xs9/WrmuQ== |
6545 | 6599 | ||
6546 | jasmine-core@~2.8.0: | 6600 | jasmine-core@~2.8.0: |
6547 | version "2.8.0" | 6601 | version "2.8.0" |
6548 | resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e" | 6602 | resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e" |
6549 | integrity sha1-vMl5rh+f0FcB5F5S5l06XWPxok4= | 6603 | integrity sha1-vMl5rh+f0FcB5F5S5l06XWPxok4= |
6550 | 6604 | ||
6605 | jasmine-core@~3.6.0: | ||
6606 | version "3.6.0" | ||
6607 | resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.6.0.tgz#491f3bb23941799c353ceb7a45b38a950ebc5a20" | ||
6608 | integrity sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw== | ||
6609 | |||
6551 | jasmine-spec-reporter@~6.0.0: | 6610 | jasmine-spec-reporter@~6.0.0: |
6552 | version "6.0.0" | 6611 | version "6.0.0" |
6553 | resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-6.0.0.tgz#3b9c85689676a351f343ba8dd6d3957f11a4bf1d" | 6612 | resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-6.0.0.tgz#3b9c85689676a351f343ba8dd6d3957f11a4bf1d" |
@@ -6785,9 +6844,9 @@ karma-source-map-support@1.4.0: | |||
6785 | source-map-support "^0.5.5" | 6844 | source-map-support "^0.5.5" |
6786 | 6845 | ||
6787 | karma@~6.1.0: | 6846 | karma@~6.1.0: |
6788 | version "6.1.1" | 6847 | version "6.1.2" |
6789 | resolved "https://registry.yarnpkg.com/karma/-/karma-6.1.1.tgz#a7539618cca0f2cbb26d5497120ec31fe340c2a1" | 6848 | resolved "https://registry.yarnpkg.com/karma/-/karma-6.1.2.tgz#9d7394559f5deb150b3021c1860960281c3a0e50" |
6790 | integrity sha512-vVDFxFGAsclgmFjZA/qGw5xqWdZIWxVD7xLyCukYUYd5xs/uGzYbXGOT5zOruVBQleKEmXIr4H2hzGCTn+M9Cg== | 6849 | integrity sha512-mKbxgsJrt3UHBPdKfCxC2eg3lpqyt6hQRFhNWJ2sk0wUnbnLPEiCpgIgiycuLSra0vC6TaK9OPJiMGATGzgH/A== |
6791 | dependencies: | 6850 | dependencies: |
6792 | body-parser "^1.19.0" | 6851 | body-parser "^1.19.0" |
6793 | braces "^3.0.2" | 6852 | braces "^3.0.2" |
@@ -7042,11 +7101,12 @@ lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.1 | |||
7042 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== | 7101 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== |
7043 | 7102 | ||
7044 | log-symbols@^4.0.0: | 7103 | log-symbols@^4.0.0: |
7045 | version "4.0.0" | 7104 | version "4.1.0" |
7046 | resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" | 7105 | resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" |
7047 | integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== | 7106 | integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== |
7048 | dependencies: | 7107 | dependencies: |
7049 | chalk "^4.0.0" | 7108 | chalk "^4.1.0" |
7109 | is-unicode-supported "^0.1.0" | ||
7050 | 7110 | ||
7051 | log4js@^6.2.1: | 7111 | log4js@^6.2.1: |
7052 | version "6.3.0" | 7112 | version "6.3.0" |
@@ -7110,9 +7170,9 @@ m3u8-parser@4.5.0: | |||
7110 | global "^4.3.2" | 7170 | global "^4.3.2" |
7111 | 7171 | ||
7112 | m3u8-parser@^4.4.0: | 7172 | m3u8-parser@^4.4.0: |
7113 | version "4.5.2" | 7173 | version "4.6.0" |
7114 | resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-4.5.2.tgz#f7d48a60112466e528324624c4e66d52ed341a75" | 7174 | resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-4.6.0.tgz#a0e2f5dcf8391c9a6e59895a084fa38f27b52124" |
7115 | integrity sha512-sN/lu3TiRxmG2RFjZxo5c0/7Dr4RrEztl43jXrWwj5gFZ7vfa2iIxGfiPx485dm5QCazaIcKk+vNkUso8Aq0Ag== | 7175 | integrity sha512-dKhhpMcPqDM/KzULVrNyDZ/z766peQjwUghDTcl6TE7DQKAt/vm74/IMUAxpO34f6LDpM+OH/dYGQwW1eM4yWw== |
7116 | dependencies: | 7176 | dependencies: |
7117 | "@babel/runtime" "^7.12.5" | 7177 | "@babel/runtime" "^7.12.5" |
7118 | "@videojs/vhs-utils" "^3.0.0" | 7178 | "@videojs/vhs-utils" "^3.0.0" |
@@ -7381,9 +7441,9 @@ mini-css-extract-plugin@1.3.5: | |||
7381 | webpack-sources "^1.1.0" | 7441 | webpack-sources "^1.1.0" |
7382 | 7442 | ||
7383 | mini-css-extract-plugin@^1.3.1: | 7443 | mini-css-extract-plugin@^1.3.1: |
7384 | version "1.3.8" | 7444 | version "1.3.9" |
7385 | resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.8.tgz#639047b78c2ee728704285aa468d2a5a8d91d566" | 7445 | resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.9.tgz#47a32132b0fd97a119acd530e8421e8f6ab16d5e" |
7386 | integrity sha512-u+2kVov/Gcs74iz+x3phEBWMAGw2djjnKfYez+Pl/b5dyXL7aM4Lp5QQtIq16CDwRHT/woUJki49gBNMhfm1eA== | 7446 | integrity sha512-Ac4s+xhVbqlyhXS5J/Vh/QXUz3ycXlCqoCPpg0vdfhsIBH9eg/It/9L1r1XhSCH737M1lqcWnMuWL13zcygn5A== |
7387 | dependencies: | 7447 | dependencies: |
7388 | loader-utils "^2.0.0" | 7448 | loader-utils "^2.0.0" |
7389 | schema-utils "^3.0.0" | 7449 | schema-utils "^3.0.0" |
@@ -7630,9 +7690,9 @@ nan@^2.12.1: | |||
7630 | integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== | 7690 | integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== |
7631 | 7691 | ||
7632 | nanoid@^3.1.20: | 7692 | nanoid@^3.1.20: |
7633 | version "3.1.20" | 7693 | version "3.1.22" |
7634 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" | 7694 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844" |
7635 | integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== | 7695 | integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ== |
7636 | 7696 | ||
7637 | nanomatch@^1.2.9: | 7697 | nanomatch@^1.2.9: |
7638 | version "1.2.13" | 7698 | version "1.2.13" |
@@ -7830,13 +7890,13 @@ npm-package-arg@8.1.0: | |||
7830 | semver "^7.0.0" | 7890 | semver "^7.0.0" |
7831 | validate-npm-package-name "^3.0.0" | 7891 | validate-npm-package-name "^3.0.0" |
7832 | 7892 | ||
7833 | npm-package-arg@^8.0.0, npm-package-arg@^8.0.1: | 7893 | npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.2: |
7834 | version "8.1.1" | 7894 | version "8.1.2" |
7835 | resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.1.tgz#00ebf16ac395c63318e67ce66780a06db6df1b04" | 7895 | resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.2.tgz#b868016ae7de5619e729993fbd8d11dc3c52ab62" |
7836 | integrity sha512-CsP95FhWQDwNqiYS+Q0mZ7FAEDytDZAkNxQqea6IaAFJTAY9Lhhqyl0irU/6PMc7BGfUmnsbHcqxJD7XuVM/rg== | 7896 | integrity sha512-6Eem455JsSMJY6Kpd3EyWE+n5hC+g9bSyHr9K9U2zqZb7+02+hObQ2c0+8iDk/mNF+8r1MhY44WypKJAkySIYA== |
7837 | dependencies: | 7897 | dependencies: |
7838 | hosted-git-info "^3.0.6" | 7898 | hosted-git-info "^4.0.1" |
7839 | semver "^7.0.0" | 7899 | semver "^7.3.4" |
7840 | validate-npm-package-name "^3.0.0" | 7900 | validate-npm-package-name "^3.0.0" |
7841 | 7901 | ||
7842 | npm-packlist@^2.1.4: | 7902 | npm-packlist@^2.1.4: |
@@ -7849,7 +7909,7 @@ npm-packlist@^2.1.4: | |||
7849 | npm-bundled "^1.1.1" | 7909 | npm-bundled "^1.1.1" |
7850 | npm-normalize-package-bin "^1.0.1" | 7910 | npm-normalize-package-bin "^1.0.1" |
7851 | 7911 | ||
7852 | npm-pick-manifest@6.1.0, npm-pick-manifest@^6.0.0: | 7912 | npm-pick-manifest@6.1.0: |
7853 | version "6.1.0" | 7913 | version "6.1.0" |
7854 | resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz#2befed87b0fce956790f62d32afb56d7539c022a" | 7914 | resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz#2befed87b0fce956790f62d32afb56d7539c022a" |
7855 | integrity sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw== | 7915 | integrity sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw== |
@@ -7858,6 +7918,16 @@ npm-pick-manifest@6.1.0, npm-pick-manifest@^6.0.0: | |||
7858 | npm-package-arg "^8.0.0" | 7918 | npm-package-arg "^8.0.0" |
7859 | semver "^7.0.0" | 7919 | semver "^7.0.0" |
7860 | 7920 | ||
7921 | npm-pick-manifest@^6.0.0: | ||
7922 | version "6.1.1" | ||
7923 | resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" | ||
7924 | integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== | ||
7925 | dependencies: | ||
7926 | npm-install-checks "^4.0.0" | ||
7927 | npm-normalize-package-bin "^1.0.1" | ||
7928 | npm-package-arg "^8.1.2" | ||
7929 | semver "^7.3.4" | ||
7930 | |||
7861 | npm-registry-fetch@^9.0.0: | 7931 | npm-registry-fetch@^9.0.0: |
7862 | version "9.0.0" | 7932 | version "9.0.0" |
7863 | resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" | 7933 | resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" |
@@ -7927,7 +7997,7 @@ object-copy@^0.1.0: | |||
7927 | define-property "^0.2.5" | 7997 | define-property "^0.2.5" |
7928 | kind-of "^3.0.3" | 7998 | kind-of "^3.0.3" |
7929 | 7999 | ||
7930 | object-inspect@^1.8.0, object-inspect@^1.9.0: | 8000 | object-inspect@^1.9.0: |
7931 | version "1.9.0" | 8001 | version "1.9.0" |
7932 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" | 8002 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" |
7933 | integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== | 8003 | integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== |
@@ -7952,7 +8022,7 @@ object-visit@^1.0.0: | |||
7952 | dependencies: | 8022 | dependencies: |
7953 | isobject "^3.0.0" | 8023 | isobject "^3.0.0" |
7954 | 8024 | ||
7955 | object.assign@^4.1.0, object.assign@^4.1.1, object.assign@^4.1.2: | 8025 | object.assign@^4.1.0, object.assign@^4.1.2: |
7956 | version "4.1.2" | 8026 | version "4.1.2" |
7957 | resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" | 8027 | resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" |
7958 | integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== | 8028 | integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== |
@@ -8896,12 +8966,12 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.27: | |||
8896 | source-map "^0.6.1" | 8966 | source-map "^0.6.1" |
8897 | supports-color "^6.1.0" | 8967 | supports-color "^6.1.0" |
8898 | 8968 | ||
8899 | postcss@^8.0.2, postcss@^8.1.4, postcss@^8.2.4: | 8969 | postcss@^8.0.2, postcss@^8.1.4, postcss@^8.2.8: |
8900 | version "8.2.6" | 8970 | version "8.2.8" |
8901 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.6.tgz#5d69a974543b45f87e464bc4c3e392a97d6be9fe" | 8971 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.8.tgz#0b90f9382efda424c4f0f69a2ead6f6830d08ece" |
8902 | integrity sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg== | 8972 | integrity sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw== |
8903 | dependencies: | 8973 | dependencies: |
8904 | colorette "^1.2.1" | 8974 | colorette "^1.2.2" |
8905 | nanoid "^3.1.20" | 8975 | nanoid "^3.1.20" |
8906 | source-map "^0.6.1" | 8976 | source-map "^0.6.1" |
8907 | 8977 | ||
@@ -8924,9 +8994,9 @@ pretty-error@^2.1.1: | |||
8924 | renderkid "^2.0.4" | 8994 | renderkid "^2.0.4" |
8925 | 8995 | ||
8926 | primeng@^11.0.0-rc.1: | 8996 | primeng@^11.0.0-rc.1: |
8927 | version "11.2.3" | 8997 | version "11.3.1" |
8928 | resolved "https://registry.yarnpkg.com/primeng/-/primeng-11.2.3.tgz#66e3d817fe27c9a7703726537c03ddcc1998bb44" | 8998 | resolved "https://registry.yarnpkg.com/primeng/-/primeng-11.3.1.tgz#644dd59d1f0808227a9529ea6ffaad31bdb5e5df" |
8929 | integrity sha512-8elRAGal8a+qXJ4egRKXU+bUvIyfCxsiCerXgOPbwbo/TU/DBK7WBXGGGi6KJOamFqClAqj/FO3WLAdofKQSRQ== | 8999 | integrity sha512-B86/su/3sNP2GfhyegvZh2MpHcUZHas+13bPL98QmZhoiPBQp2jz3H0iD716+piC00Wee6pi/PPm7e9y9qxGDg== |
8930 | dependencies: | 9000 | dependencies: |
8931 | tslib "^2.0.0" | 9001 | tslib "^2.0.0" |
8932 | 9002 | ||
@@ -9027,11 +9097,6 @@ public-encrypt@^4.0.0: | |||
9027 | randombytes "^2.0.1" | 9097 | randombytes "^2.0.1" |
9028 | safe-buffer "^5.1.2" | 9098 | safe-buffer "^5.1.2" |
9029 | 9099 | ||
9030 | puka@^1.0.1: | ||
9031 | version "1.0.1" | ||
9032 | resolved "https://registry.yarnpkg.com/puka/-/puka-1.0.1.tgz#a2df782b7eb4cf9564e4c93a5da422de0dfacc02" | ||
9033 | integrity sha512-ssjRZxBd7BT3dte1RR3VoeT2cT/ODH8x+h0rUF1rMqB0srHYf48stSDWfiYakTp5UBZMxroZhB2+ExLDHm7W3g== | ||
9034 | |||
9035 | pump@^2.0.0: | 9100 | pump@^2.0.0: |
9036 | version "2.0.1" | 9101 | version "2.0.1" |
9037 | resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" | 9102 | resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" |
@@ -9133,15 +9198,15 @@ querystringify@^2.1.1: | |||
9133 | resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" | 9198 | resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" |
9134 | integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== | 9199 | integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== |
9135 | 9200 | ||
9136 | queue-microtask@^1.1.2, queue-microtask@^1.2.0, queue-microtask@^1.2.2: | 9201 | queue-microtask@^1.2.0, queue-microtask@^1.2.2: |
9137 | version "1.2.2" | 9202 | version "1.2.3" |
9138 | resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.2.tgz#abf64491e6ecf0f38a6502403d4cda04f372dfd3" | 9203 | resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" |
9139 | integrity sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg== | 9204 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== |
9140 | 9205 | ||
9141 | random-access-file@^2.0.1: | 9206 | random-access-file@^2.0.1: |
9142 | version "2.1.5" | 9207 | version "2.2.0" |
9143 | resolved "https://registry.yarnpkg.com/random-access-file/-/random-access-file-2.1.5.tgz#27af6115b920a9adabb44559e29ea9944bb35bfe" | 9208 | resolved "https://registry.yarnpkg.com/random-access-file/-/random-access-file-2.2.0.tgz#b49b999efefb374afb7587f219071fec5ce66546" |
9144 | integrity sha512-lqmUGgF9X+LD0XSeWSHcs7U2nSLYp+RQvkDDqKWoxW8jcd13tZ00G6PHV32OZqDIHmS9ewoEUEa6jcvyB7UCvg== | 9209 | integrity sha512-B744003Mj7v3EcuPl9hCiB2Ot4aZjgtU2mV6yFY1THiWU/XfGf1uSadR+SlQdJcwHgAWeG7Lbos0aUqjtj8FQg== |
9145 | dependencies: | 9210 | dependencies: |
9146 | mkdirp-classic "^0.5.2" | 9211 | mkdirp-classic "^0.5.2" |
9147 | random-access-storage "^1.1.1" | 9212 | random-access-storage "^1.1.1" |
@@ -9370,9 +9435,9 @@ regjsgen@^0.5.1: | |||
9370 | integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== | 9435 | integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== |
9371 | 9436 | ||
9372 | regjsparser@^0.6.4: | 9437 | regjsparser@^0.6.4: |
9373 | version "0.6.7" | 9438 | version "0.6.8" |
9374 | resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.7.tgz#c00164e1e6713c2e3ee641f1701c4b7aa0a7f86c" | 9439 | resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.8.tgz#4532c3da36d75d56e3f394ce2ea6842bde7496bd" |
9375 | integrity sha512-ib77G0uxsA2ovgiYbCVGx4Pv3PSttAx2vIwidqQzbL2U5S4Q+j00HdSAneSBuyVcMvEnTXMjiGgB+DlXozVhpQ== | 9440 | integrity sha512-3weFrFQREJhJ2PW+iCGaG6TenyzNSZgsBKZ/oEf6Trme31COSeIWhHw9O6FPkuXktfx+b6Hf/5e6dKPHaROq2g== |
9376 | dependencies: | 9441 | dependencies: |
9377 | jsesc "~0.5.0" | 9442 | jsesc "~0.5.0" |
9378 | 9443 | ||
@@ -9593,9 +9658,9 @@ rework@1.0.1, rework@^1.0.1: | |||
9593 | css "^2.0.0" | 9658 | css "^2.0.0" |
9594 | 9659 | ||
9595 | rfdc@^1.1.4: | 9660 | rfdc@^1.1.4: |
9596 | version "1.2.0" | 9661 | version "1.3.0" |
9597 | resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.2.0.tgz#9e9894258f48f284b43c3143c68070a4f373b949" | 9662 | resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" |
9598 | integrity sha512-ijLyszTMmUrXvjSooucVQwimGUk84eRcmCuLV8Xghe3UO85mjUtRAHRyoMM6XtyqbECaXuBWx18La3523sXINA== | 9663 | integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== |
9599 | 9664 | ||
9600 | rgb-regex@^1.0.1: | 9665 | rgb-regex@^1.0.1: |
9601 | version "1.0.1" | 9666 | version "1.0.1" |
@@ -9681,7 +9746,7 @@ run-series@^1.1.8, run-series@^1.1.9: | |||
9681 | resolved "https://registry.yarnpkg.com/run-series/-/run-series-1.1.9.tgz#15ba9cb90e6a6c054e67c98e1dc063df0ecc113a" | 9746 | resolved "https://registry.yarnpkg.com/run-series/-/run-series-1.1.9.tgz#15ba9cb90e6a6c054e67c98e1dc063df0ecc113a" |
9682 | integrity sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g== | 9747 | integrity sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g== |
9683 | 9748 | ||
9684 | rusha@^0.8.1: | 9749 | rusha@^0.8.13: |
9685 | version "0.8.13" | 9750 | version "0.8.13" |
9686 | resolved "https://registry.yarnpkg.com/rusha/-/rusha-0.8.13.tgz#9a084e7b860b17bff3015b92c67a6a336191513a" | 9751 | resolved "https://registry.yarnpkg.com/rusha/-/rusha-0.8.13.tgz#9a084e7b860b17bff3015b92c67a6a336191513a" |
9687 | integrity sha1-mghOe4YLF7/zAVuSxnpqM2GRUTo= | 9752 | integrity sha1-mghOe4YLF7/zAVuSxnpqM2GRUTo= |
@@ -9742,9 +9807,9 @@ safe-regex@^1.1.0: | |||
9742 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== | 9807 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== |
9743 | 9808 | ||
9744 | sanitize-html@^2.1.2: | 9809 | sanitize-html@^2.1.2: |
9745 | version "2.3.2" | 9810 | version "2.3.3" |
9746 | resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.3.2.tgz#a1954aea877a096c408aca7b0c260bef6e4fc402" | 9811 | resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.3.3.tgz#3db382c9a621cce4c46d90f10c64f1e9da9e8353" |
9747 | integrity sha512-p7neuskvC8pSurUjdVmbWPXmc9A4+QpOXIL+4gwFC+av5h+lYCXFT8uEneqsFQg/wEA1IH+cKQA60AaQI6p3cg== | 9812 | integrity sha512-DCFXPt7Di0c6JUnlT90eIgrjs6TsJl/8HYU3KLdmrVclFN4O0heTcVbJiMa23OKVr6aR051XYtsgd8EWwEBwUA== |
9748 | dependencies: | 9813 | dependencies: |
9749 | deepmerge "^4.2.2" | 9814 | deepmerge "^4.2.2" |
9750 | escape-string-regexp "^4.0.0" | 9815 | escape-string-regexp "^4.0.0" |
@@ -9894,7 +9959,7 @@ semver@7.0.0: | |||
9894 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" | 9959 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" |
9895 | integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== | 9960 | integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== |
9896 | 9961 | ||
9897 | semver@7.3.4, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4: | 9962 | semver@7.3.4: |
9898 | version "7.3.4" | 9963 | version "7.3.4" |
9899 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" | 9964 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" |
9900 | integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== | 9965 | integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== |
@@ -9906,6 +9971,13 @@ semver@^6.0.0, semver@^6.3.0: | |||
9906 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" | 9971 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" |
9907 | integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== | 9972 | integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== |
9908 | 9973 | ||
9974 | semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4: | ||
9975 | version "7.3.5" | ||
9976 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" | ||
9977 | integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== | ||
9978 | dependencies: | ||
9979 | lru-cache "^6.0.0" | ||
9980 | |||
9909 | send@0.17.1: | 9981 | send@0.17.1: |
9910 | version "0.17.1" | 9982 | version "0.17.1" |
9911 | resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" | 9983 | resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" |
@@ -10061,9 +10133,9 @@ simple-get@^4.0.0: | |||
10061 | simple-concat "^1.0.0" | 10133 | simple-concat "^1.0.0" |
10062 | 10134 | ||
10063 | simple-peer@^9.5.0, simple-peer@^9.7.1, simple-peer@^9.9.3: | 10135 | simple-peer@^9.5.0, simple-peer@^9.7.1, simple-peer@^9.9.3: |
10064 | version "9.9.3" | 10136 | version "9.10.0" |
10065 | resolved "https://registry.yarnpkg.com/simple-peer/-/simple-peer-9.9.3.tgz#b52c39d1173620d06c8b29ada7ee2ad3384bb469" | 10137 | resolved "https://registry.yarnpkg.com/simple-peer/-/simple-peer-9.10.0.tgz#f458444300f635e6fcc2f5a5166c45d71eafb57f" |
10066 | integrity sha512-T3wuv0UqBpDTV0x0pJPPsz4thy0tC0fTOHE4g9+AF43RUxxT+MWeXVtdQcK5Xuzv/XTVrB2NrGzdfO1IFBqOkw== | 10138 | integrity sha512-sKrKtca1UdmwdZIbvuT3iEL05tDGt/xdLP6+ej8rh1ADgtDk44yLaEZjIyPJ6c34zsSih46Ou7zUIT7e4hPK7g== |
10067 | dependencies: | 10139 | dependencies: |
10068 | buffer "^6.0.2" | 10140 | buffer "^6.0.2" |
10069 | debug "^4.2.0" | 10141 | debug "^4.2.0" |
@@ -10074,12 +10146,12 @@ simple-peer@^9.5.0, simple-peer@^9.7.1, simple-peer@^9.9.3: | |||
10074 | readable-stream "^3.6.0" | 10146 | readable-stream "^3.6.0" |
10075 | 10147 | ||
10076 | simple-sha1@^3.0.0, simple-sha1@^3.0.1: | 10148 | simple-sha1@^3.0.0, simple-sha1@^3.0.1: |
10077 | version "3.0.1" | 10149 | version "3.1.0" |
10078 | resolved "https://registry.yarnpkg.com/simple-sha1/-/simple-sha1-3.0.1.tgz#b34c3c978d74ac4baf99b6555c1e6736e0d6e700" | 10150 | resolved "https://registry.yarnpkg.com/simple-sha1/-/simple-sha1-3.1.0.tgz#40cac8436dfaf9924332fc46a5c7bca45f656131" |
10079 | integrity sha512-q7ehqWfHc1VhOm7sW099YDZ4I0yYX7rqyhqqhHV1IYeUTjPOhHyD3mXvv8k2P+rO7+7c8R4/D+8ffzC9BE7Cqg== | 10151 | integrity sha512-ArTptMRC1v08H8ihPD6l0wesKvMfF9e8XL5rIHPanI7kGOsSsbY514MwVu6X1PITHCTB2F08zB7cyEbfc4wQjg== |
10080 | dependencies: | 10152 | dependencies: |
10081 | queue-microtask "^1.1.2" | 10153 | queue-microtask "^1.2.2" |
10082 | rusha "^0.8.1" | 10154 | rusha "^0.8.13" |
10083 | 10155 | ||
10084 | simple-swizzle@^0.2.2: | 10156 | simple-swizzle@^0.2.2: |
10085 | version "0.2.2" | 10157 | version "0.2.2" |
@@ -10159,9 +10231,9 @@ socket.io-adapter@~2.1.0: | |||
10159 | integrity sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg== | 10231 | integrity sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg== |
10160 | 10232 | ||
10161 | socket.io-client@^3.0.3: | 10233 | socket.io-client@^3.0.3: |
10162 | version "3.1.1" | 10234 | version "3.1.3" |
10163 | resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-3.1.1.tgz#43dfc3feddbb675b274a724f685d6b6af319b3e3" | 10235 | resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-3.1.3.tgz#57ddcefea58cfab71f0e94c21124de8e3c5aa3e2" |
10164 | integrity sha512-BLgIuCjI7Sf3mDHunKddX9zKR/pbkP7IACM3sJS3jha+zJ6/pGKRV6Fz5XSBHCfUs9YzT8kYIqNwOOuFNLtnYA== | 10236 | integrity sha512-4sIGOGOmCg3AOgGi7EEr6ZkTZRkrXwub70bBB/F0JSkMOUFpA77WsL87o34DffQQ31PkbMUIadGOk+3tx1KGbw== |
10165 | dependencies: | 10237 | dependencies: |
10166 | "@types/component-emitter" "^1.2.10" | 10238 | "@types/component-emitter" "^1.2.10" |
10167 | backo2 "~1.0.2" | 10239 | backo2 "~1.0.2" |
@@ -10181,13 +10253,13 @@ socket.io-parser@~4.0.3, socket.io-parser@~4.0.4: | |||
10181 | debug "~4.3.1" | 10253 | debug "~4.3.1" |
10182 | 10254 | ||
10183 | socket.io@^3.1.0: | 10255 | socket.io@^3.1.0: |
10184 | version "3.1.1" | 10256 | version "3.1.2" |
10185 | resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-3.1.1.tgz#905e3d4a3b37d8e7970e67a4a6eb81110a5778ba" | 10257 | resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-3.1.2.tgz#06e27caa1c4fc9617547acfbb5da9bc1747da39a" |
10186 | integrity sha512-7cBWdsDC7bbyEF6WbBqffjizc/H4YF1wLdZoOzuYfo2uMNSFjJKuQ36t0H40o9B20DO6p+mSytEd92oP4S15bA== | 10258 | integrity sha512-JubKZnTQ4Z8G4IZWtaAZSiRP3I/inpy8c/Bsx2jrwGrTbKeVU5xd6qkKMHpChYeM3dWZSO0QACiGK+obhBNwYw== |
10187 | dependencies: | 10259 | dependencies: |
10188 | "@types/cookie" "^0.4.0" | 10260 | "@types/cookie" "^0.4.0" |
10189 | "@types/cors" "^2.8.8" | 10261 | "@types/cors" "^2.8.8" |
10190 | "@types/node" "^14.14.10" | 10262 | "@types/node" ">=10.0.0" |
10191 | accepts "~1.3.4" | 10263 | accepts "~1.3.4" |
10192 | base64id "~2.0.0" | 10264 | base64id "~2.0.0" |
10193 | debug "~4.3.1" | 10265 | debug "~4.3.1" |
@@ -10226,9 +10298,9 @@ socks-proxy-agent@^5.0.0: | |||
10226 | socks "^2.3.3" | 10298 | socks "^2.3.3" |
10227 | 10299 | ||
10228 | socks@^2.3.3: | 10300 | socks@^2.3.3: |
10229 | version "2.5.1" | 10301 | version "2.6.0" |
10230 | resolved "https://registry.yarnpkg.com/socks/-/socks-2.5.1.tgz#7720640b6b5ec9a07d556419203baa3f0596df5f" | 10302 | resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.0.tgz#6b984928461d39871b3666754b9000ecf39dfac2" |
10231 | integrity sha512-oZCsJJxapULAYJaEYBSzMcz8m3jqgGrHaGhkmU/o/PQfFWYWxkAaA0UMGImb6s6tEXfKi959X6VJjMMQ3P6TTQ== | 10303 | integrity sha512-mNmr9owlinMplev0Wd7UHFlqI4ofnBnNzFuzrm63PPaHgbkqCFe4T5LzwKmtQ/f2tX0NTpcdVLyD/FHxFBstYw== |
10232 | dependencies: | 10304 | dependencies: |
10233 | ip "^1.1.5" | 10305 | ip "^1.1.5" |
10234 | smart-buffer "^4.1.0" | 10306 | smart-buffer "^4.1.0" |
@@ -10416,7 +10488,7 @@ ssri@^6.0.1: | |||
10416 | dependencies: | 10488 | dependencies: |
10417 | figgy-pudding "^3.5.1" | 10489 | figgy-pudding "^3.5.1" |
10418 | 10490 | ||
10419 | ssri@^8.0.0: | 10491 | ssri@^8.0.0, ssri@^8.0.1: |
10420 | version "8.0.1" | 10492 | version "8.0.1" |
10421 | resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" | 10493 | resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" |
10422 | integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== | 10494 | integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== |
@@ -10546,15 +10618,15 @@ string-width@^3.0.0, string-width@^3.1.0: | |||
10546 | strip-ansi "^5.1.0" | 10618 | strip-ansi "^5.1.0" |
10547 | 10619 | ||
10548 | string-width@^4.1.0, string-width@^4.2.0: | 10620 | string-width@^4.1.0, string-width@^4.2.0: |
10549 | version "4.2.0" | 10621 | version "4.2.2" |
10550 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" | 10622 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" |
10551 | integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== | 10623 | integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== |
10552 | dependencies: | 10624 | dependencies: |
10553 | emoji-regex "^8.0.0" | 10625 | emoji-regex "^8.0.0" |
10554 | is-fullwidth-code-point "^3.0.0" | 10626 | is-fullwidth-code-point "^3.0.0" |
10555 | strip-ansi "^6.0.0" | 10627 | strip-ansi "^6.0.0" |
10556 | 10628 | ||
10557 | string.prototype.trimend@^1.0.1, string.prototype.trimend@^1.0.3: | 10629 | string.prototype.trimend@^1.0.4: |
10558 | version "1.0.4" | 10630 | version "1.0.4" |
10559 | resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" | 10631 | resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" |
10560 | integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== | 10632 | integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== |
@@ -10562,7 +10634,7 @@ string.prototype.trimend@^1.0.1, string.prototype.trimend@^1.0.3: | |||
10562 | call-bind "^1.0.2" | 10634 | call-bind "^1.0.2" |
10563 | define-properties "^1.1.3" | 10635 | define-properties "^1.1.3" |
10564 | 10636 | ||
10565 | string.prototype.trimstart@^1.0.1, string.prototype.trimstart@^1.0.3: | 10637 | string.prototype.trimstart@^1.0.4: |
10566 | version "1.0.4" | 10638 | version "1.0.4" |
10567 | resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" | 10639 | resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" |
10568 | integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== | 10640 | integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== |
@@ -10815,9 +10887,9 @@ terser@^4.1.2, terser@^4.6.3: | |||
10815 | source-map-support "~0.5.12" | 10887 | source-map-support "~0.5.12" |
10816 | 10888 | ||
10817 | terser@^5.3.4: | 10889 | terser@^5.3.4: |
10818 | version "5.6.0" | 10890 | version "5.6.1" |
10819 | resolved "https://registry.yarnpkg.com/terser/-/terser-5.6.0.tgz#138cdf21c5e3100b1b3ddfddf720962f88badcd2" | 10891 | resolved "https://registry.yarnpkg.com/terser/-/terser-5.6.1.tgz#a48eeac5300c0a09b36854bf90d9c26fb201973c" |
10820 | integrity sha512-vyqLMoqadC1uR0vywqOZzriDYzgEkNJFK4q9GeyOBHIbiECHiWLKcWfbQWAUaPfxkjDhapSlZB9f7fkMrvkVjA== | 10892 | integrity sha512-yv9YLFQQ+3ZqgWCUk+pvNJwgUTdlIxUk1WTN+RnaFJe2L7ipG2csPT0ra2XRm7Cs8cxN7QXmK1rFzEwYEQkzXw== |
10821 | dependencies: | 10893 | dependencies: |
10822 | commander "^2.20.0" | 10894 | commander "^2.20.0" |
10823 | source-map "~0.7.2" | 10895 | source-map "~0.7.2" |
@@ -10976,9 +11048,9 @@ tree-kill@1.2.2: | |||
10976 | integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== | 11048 | integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== |
10977 | 11049 | ||
10978 | ts-loader@^8.0.14: | 11050 | ts-loader@^8.0.14: |
10979 | version "8.0.17" | 11051 | version "8.0.18" |
10980 | resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.17.tgz#98f2ccff9130074f4079fd89b946b4c637b1f2fc" | 11052 | resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.18.tgz#b2385cbe81c34ad9f997915129cdde3ad92a61ea" |
10981 | integrity sha512-OeVfSshx6ot/TCxRwpBHQ/4lRzfgyTkvi7ghDVrLXOHzTbSK413ROgu/xNqM72i3AFeAIJgQy78FwSMKmOW68w== | 11053 | integrity sha512-hRZzkydPX30XkLaQwJTDcWDoxZHK6IrEMDQpNd7tgcakFruFkeUp/aY+9hBb7BUGb+ZWKI0jiOGMo0MckwzdDQ== |
10982 | dependencies: | 11054 | dependencies: |
10983 | chalk "^4.1.0" | 11055 | chalk "^4.1.0" |
10984 | enhanced-resolve "^4.0.0" | 11056 | enhanced-resolve "^4.0.0" |
@@ -11054,9 +11126,9 @@ tsutils@^2.29.0: | |||
11054 | tslib "^1.8.1" | 11126 | tslib "^1.8.1" |
11055 | 11127 | ||
11056 | tsutils@^3.0.0: | 11128 | tsutils@^3.0.0: |
11057 | version "3.20.0" | 11129 | version "3.21.0" |
11058 | resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.20.0.tgz#ea03ea45462e146b53d70ce0893de453ff24f698" | 11130 | resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" |
11059 | integrity sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg== | 11131 | integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== |
11060 | dependencies: | 11132 | dependencies: |
11061 | tslib "^1.8.1" | 11133 | tslib "^1.8.1" |
11062 | 11134 | ||
@@ -11103,9 +11175,9 @@ type@^1.0.1: | |||
11103 | integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== | 11175 | integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== |
11104 | 11176 | ||
11105 | type@^2.0.0: | 11177 | type@^2.0.0: |
11106 | version "2.3.0" | 11178 | version "2.5.0" |
11107 | resolved "https://registry.yarnpkg.com/type/-/type-2.3.0.tgz#ada7c045f07ead08abf9e2edd29be1a0c0661132" | 11179 | resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" |
11108 | integrity sha512-rgPIqOdfK/4J9FhiVrZ3cveAjRRo5rsQBAIhnylX874y1DX/kEKSVdLsnuHB6l1KTjHyU01VjiMBHgU2adejyg== | 11180 | integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== |
11109 | 11181 | ||
11110 | typedarray-to-buffer@^3.0.0: | 11182 | typedarray-to-buffer@^3.0.0: |
11111 | version "3.1.5" | 11183 | version "3.1.5" |
@@ -11119,12 +11191,7 @@ typedarray@^0.0.6: | |||
11119 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" | 11191 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" |
11120 | integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= | 11192 | integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= |
11121 | 11193 | ||
11122 | typescript@4.1.3: | 11194 | typescript@4.1.5, typescript@~4.1.3: |
11123 | version "4.1.3" | ||
11124 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" | ||
11125 | integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== | ||
11126 | |||
11127 | typescript@~4.1.3: | ||
11128 | version "4.1.5" | 11195 | version "4.1.5" |
11129 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72" | 11196 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72" |
11130 | integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA== | 11197 | integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA== |
@@ -11140,9 +11207,9 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: | |||
11140 | integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== | 11207 | integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== |
11141 | 11208 | ||
11142 | uglify-js@^3.0.6: | 11209 | uglify-js@^3.0.6: |
11143 | version "3.12.8" | 11210 | version "3.13.2" |
11144 | resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.12.8.tgz#a82e6e53c9be14f7382de3d068ef1e26e7d4aaf8" | 11211 | resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.2.tgz#fe10319861bccc8682bfe2e8151fbdd8aa921c44" |
11145 | integrity sha512-fvBeuXOsvqjecUtF/l1dwsrrf5y2BCUk9AOJGzGcm6tE7vegku5u/YvqjyDaAGr422PLoLnrxg3EnRvTqsdC1w== | 11212 | integrity sha512-SbMu4D2Vo95LMC/MetNaso1194M1htEA+JrqE9Hk+G2DhI+itfS9TRu9ZKeCahLDNa/J3n4MqUJ/fOHMzQpRWw== |
11146 | 11213 | ||
11147 | uint64be@^2.0.2: | 11214 | uint64be@^2.0.2: |
11148 | version "2.0.2" | 11215 | version "2.0.2" |
@@ -11151,6 +11218,16 @@ uint64be@^2.0.2: | |||
11151 | dependencies: | 11218 | dependencies: |
11152 | buffer-alloc "^1.1.0" | 11219 | buffer-alloc "^1.1.0" |
11153 | 11220 | ||
11221 | unbox-primitive@^1.0.0: | ||
11222 | version "1.0.0" | ||
11223 | resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.0.tgz#eeacbc4affa28e9b3d36b5eaeccc50b3251b1d3f" | ||
11224 | integrity sha512-P/51NX+JXyxK/aigg1/ZgyccdAxm5K1+n8+tvqSntjOivPt19gvm1VC49RWYetsiub8WViUchdxl/KWHHB0kzA== | ||
11225 | dependencies: | ||
11226 | function-bind "^1.1.1" | ||
11227 | has-bigints "^1.0.0" | ||
11228 | has-symbols "^1.0.0" | ||
11229 | which-boxed-primitive "^1.0.1" | ||
11230 | |||
11154 | unicode-canonical-property-names-ecmascript@^1.0.4: | 11231 | unicode-canonical-property-names-ecmascript@^1.0.4: |
11155 | version "1.0.4" | 11232 | version "1.0.4" |
11156 | resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" | 11233 | resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" |
@@ -11402,9 +11479,9 @@ uuid@^3.0.0, uuid@^3.3.2, uuid@^3.4.0: | |||
11402 | integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== | 11479 | integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== |
11403 | 11480 | ||
11404 | v8-compile-cache@^2.2.0: | 11481 | v8-compile-cache@^2.2.0: |
11405 | version "2.2.0" | 11482 | version "2.3.0" |
11406 | resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" | 11483 | resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" |
11407 | integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== | 11484 | integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== |
11408 | 11485 | ||
11409 | validate-npm-package-license@^3.0.1: | 11486 | validate-npm-package-license@^3.0.1: |
11410 | version "3.0.4" | 11487 | version "3.0.4" |
@@ -11801,15 +11878,26 @@ webtorrent@^0.112.3: | |||
11801 | utp-native "^2.3.0" | 11878 | utp-native "^2.3.0" |
11802 | 11879 | ||
11803 | whatwg-fetch@^3.0.0: | 11880 | whatwg-fetch@^3.0.0: |
11804 | version "3.6.1" | 11881 | version "3.6.2" |
11805 | resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.1.tgz#93bc4005af6c2cc30ba3e42ec3125947c8f54ed3" | 11882 | resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" |
11806 | integrity sha512-IEmN/ZfmMw6G1hgZpVd0LuZXOQDisrMOZrzYd5x3RAK4bMPlJohKUZWZ9t/QsTvH0dV9TbPDcc2OSuIDcihnHA== | 11883 | integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== |
11807 | 11884 | ||
11808 | whatwg-mimetype@^2.3.0: | 11885 | whatwg-mimetype@^2.3.0: |
11809 | version "2.3.0" | 11886 | version "2.3.0" |
11810 | resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" | 11887 | resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" |
11811 | integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== | 11888 | integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== |
11812 | 11889 | ||
11890 | which-boxed-primitive@^1.0.1: | ||
11891 | version "1.0.2" | ||
11892 | resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" | ||
11893 | integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== | ||
11894 | dependencies: | ||
11895 | is-bigint "^1.0.1" | ||
11896 | is-boolean-object "^1.1.0" | ||
11897 | is-number-object "^1.0.4" | ||
11898 | is-string "^1.0.5" | ||
11899 | is-symbol "^1.0.3" | ||
11900 | |||
11813 | which-module@^2.0.0: | 11901 | which-module@^2.0.0: |
11814 | version "2.0.0" | 11902 | version "2.0.0" |
11815 | resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" | 11903 | resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" |
@@ -11915,9 +12003,9 @@ ws@^6.2.1: | |||
11915 | async-limiter "~1.0.0" | 12003 | async-limiter "~1.0.0" |
11916 | 12004 | ||
11917 | ws@^7.3.0, ws@^7.3.1, ws@^7.4.2, ws@~7.4.2: | 12005 | ws@^7.3.0, ws@^7.3.1, ws@^7.4.2, ws@~7.4.2: |
11918 | version "7.4.3" | 12006 | version "7.4.4" |
11919 | resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd" | 12007 | resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" |
11920 | integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA== | 12008 | integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== |
11921 | 12009 | ||
11922 | xml2js@^0.4.17: | 12010 | xml2js@^0.4.17: |
11923 | version "0.4.23" | 12011 | version "0.4.23" |
@@ -11978,9 +12066,9 @@ yallist@^4.0.0: | |||
11978 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== | 12066 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== |
11979 | 12067 | ||
11980 | yaml@^1.10.0: | 12068 | yaml@^1.10.0: |
11981 | version "1.10.0" | 12069 | version "1.10.2" |
11982 | resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" | 12070 | resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" |
11983 | integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== | 12071 | integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== |
11984 | 12072 | ||
11985 | yargs-parser@^13.1.2: | 12073 | yargs-parser@^13.1.2: |
11986 | version "13.1.2" | 12074 | version "13.1.2" |
@@ -11999,9 +12087,9 @@ yargs-parser@^18.1.2: | |||
11999 | decamelize "^1.2.0" | 12087 | decamelize "^1.2.0" |
12000 | 12088 | ||
12001 | yargs-parser@^20.2.2: | 12089 | yargs-parser@^20.2.2: |
12002 | version "20.2.6" | 12090 | version "20.2.7" |
12003 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.6.tgz#69f920addf61aafc0b8b89002f5d66e28f2d8b20" | 12091 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" |
12004 | integrity sha512-AP1+fQIWSM/sMiET8fyayjx/J+JmTPt2Mr0FkrgqB4todtfa53sOsrSAcIrJRD5XS20bKUwaDIuMkWKCEiQLKA== | 12092 | integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== |
12005 | 12093 | ||
12006 | yargs-parser@^7.0.0: | 12094 | yargs-parser@^7.0.0: |
12007 | version "7.0.0" | 12095 | version "7.0.0" |