aboutsummaryrefslogtreecommitdiffhomepage
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/src/app/+accounts/account-video-channels/account-video-channels.component.html26
-rw-r--r--client/src/app/+accounts/account-video-channels/account-video-channels.component.scss31
-rw-r--r--client/src/app/+accounts/account-video-channels/account-video-channels.component.ts71
-rw-r--r--client/src/app/+accounts/account-videos/account-videos.component.ts2
-rw-r--r--client/src/app/+accounts/accounts-routing.module.ts2
-rw-r--r--client/src/app/+accounts/accounts.component.html4
-rw-r--r--client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts5
-rw-r--r--client/src/app/shared/video-channel/video-channel.service.ts17
-rw-r--r--client/src/app/shared/video/abstract-video-list.html2
-rw-r--r--client/src/app/shared/video/abstract-video-list.scss28
-rw-r--r--client/src/app/videos/video-list/video-overview.component.html6
-rw-r--r--client/src/app/videos/video-list/video-overview.component.scss60
-rw-r--r--client/src/sass/include/_miniature.scss95
13 files changed, 207 insertions, 142 deletions
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 c3ef1d894..63f0514fd 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,11 +1,17 @@
1<div *ngIf="account" class="row"> 1<div class="margin-content">
2 <a
3 *ngFor="let videoChannel of videoChannels" [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]"
4 class="video-channel" i18n-title title="See this video channel"
5 >
6 <img [src]="videoChannel.avatarUrl" alt="Avatar" />
7 2
8 <div class="video-channel-display-name">{{ videoChannel.displayName }}</div> 3 <div class="channels" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true">
9 <div i18n class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div> 4 <div class="section channel" *ngFor="let videoChannel of videoChannels">
10 </a> 5 <div class="section-title">
11</div> \ No newline at end of file 6 <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]" i18n-title title="See this video channel">
7 <img [src]="videoChannel.avatarUrl" alt="Avatar" />
8
9 <div>{{ videoChannel.displayName }}</div>
10 <div i18n class="followers">{{ videoChannel.followersCount }} subscribers</div>
11 </a>
12 </div>
13
14 <my-video-miniature *ngFor="let video of getVideosOf(videoChannel)" [video]="video" [user]="user" [displayVideoActions]="false"></my-video-miniature>
15 </div>
16 </div>
17</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 0c6de2efa..f2604684e 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
@@ -1,30 +1,13 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3@import '_miniature';
3 4
4.row { 5.margin-content {
5 justify-content: center; 6 @include adapt-margin-content-width;
6} 7}
7 8
8a.video-channel { 9.section {
9 @include disable-default-a-behaviour; 10 @include miniature-rows;
10 11
11 display: inline-block; 12 padding-top: 0 !important;
12 text-align: center; 13}
13 color: var(--mainForegroundColor);
14 margin: 10px 30px;
15
16 img {
17 @include avatar(80px);
18
19 margin-bottom: 10px;
20 }
21
22 .video-channel-display-name {
23 font-size: 20px;
24 font-weight: $font-bold;
25 }
26
27 .video-channel-followers {
28 font-size: 15px;
29 }
30} \ No newline at end of file
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 44f5626bb..ee3b5f8e4 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
@@ -3,9 +3,14 @@ import { ActivatedRoute } from '@angular/router'
3import { Account } from '@app/shared/account/account.model' 3import { Account } from '@app/shared/account/account.model'
4import { AccountService } from '@app/shared/account/account.service' 4import { AccountService } from '@app/shared/account/account.service'
5import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' 5import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
6import { flatMap, map, tap } from 'rxjs/operators' 6import { concatMap, map, switchMap, tap } from 'rxjs/operators'
7import { Subscription } from 'rxjs' 7import { from, Subscription } from 'rxjs'
8import { VideoChannel } from '@app/shared/video-channel/video-channel.model' 8import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
9import { Video } from '@app/shared/video/video.model'
10import { AuthService } from '@app/core'
11import { VideoService } from '@app/shared/video/video.service'
12import { VideoSortField } from '@app/shared/video/sort-field.type'
13import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model'
9 14
10@Component({ 15@Component({
11 selector: 'my-account-video-channels', 16 selector: 'my-account-video-channels',
@@ -15,27 +20,73 @@ import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
15export class AccountVideoChannelsComponent implements OnInit, OnDestroy { 20export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
16 account: Account 21 account: Account
17 videoChannels: VideoChannel[] = [] 22 videoChannels: VideoChannel[] = []
23 videos: { [id: number]: Video[] } = {}
24
25 channelPagination: ComponentPagination = {
26 currentPage: 1,
27 itemsPerPage: 2
28 }
29
30 videosPagination: ComponentPagination = {
31 currentPage: 1,
32 itemsPerPage: 12
33 }
34 videosSort: VideoSortField = '-publishedAt'
18 35
19 private accountSub: Subscription 36 private accountSub: Subscription
20 37
21 constructor ( 38 constructor (
22 protected route: ActivatedRoute, 39 private route: ActivatedRoute,
40 private authService: AuthService,
23 private accountService: AccountService, 41 private accountService: AccountService,
24 private videoChannelService: VideoChannelService 42 private videoChannelService: VideoChannelService,
43 private videoService: VideoService
25 ) { } 44 ) { }
26 45
46 get user () {
47 return this.authService.getUser()
48 }
49
27 ngOnInit () { 50 ngOnInit () {
28 // Parent get the account for us 51 // Parent get the account for us
29 this.accountSub = this.accountService.accountLoaded 52 this.accountSub = this.accountService.accountLoaded
30 .pipe( 53 .subscribe(account => {
31 tap(account => this.account = account), 54 this.account = account
32 flatMap(account => this.videoChannelService.listAccountVideoChannels(account)), 55
33 map(res => res.data) 56 this.loadMoreChannels()
34 ) 57 })
35 .subscribe(videoChannels => this.videoChannels = videoChannels)
36 } 58 }
37 59
38 ngOnDestroy () { 60 ngOnDestroy () {
39 if (this.accountSub) this.accountSub.unsubscribe() 61 if (this.accountSub) this.accountSub.unsubscribe()
40 } 62 }
63
64 loadMoreChannels () {
65 this.videoChannelService.listAccountVideoChannels(this.account, this.channelPagination)
66 .pipe(
67 tap(res => this.channelPagination.totalItems = res.total),
68 switchMap(res => from(res.data)),
69 concatMap(videoChannel => {
70 return this.videoService.getVideoChannelVideos(videoChannel, this.videosPagination, this.videosSort)
71 .pipe(map(data => ({ videoChannel, videos: data.videos })))
72 })
73 )
74 .subscribe(({ videoChannel, videos }) => {
75 this.videoChannels.push(videoChannel)
76
77 this.videos[videoChannel.id] = videos
78 })
79 }
80
81 getVideosOf (videoChannel: VideoChannel) {
82 return this.videos[ videoChannel.id ] || []
83 }
84
85 onNearOfBottom () {
86 if (!hasMoreItems(this.channelPagination)) return
87
88 this.channelPagination.currentPage += 1
89
90 this.loadMoreChannels()
91 }
41} 92}
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 0d579fa0c..6d26a4322 100644
--- a/client/src/app/+accounts/account-videos/account-videos.component.ts
+++ b/client/src/app/+accounts/account-videos/account-videos.component.ts
@@ -41,8 +41,6 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit,
41 private videoService: VideoService 41 private videoService: VideoService
42 ) { 42 ) {
43 super() 43 super()
44
45 this.titlePage = this.i18n('Published videos')
46 } 44 }
47 45
48 ngOnInit () { 46 ngOnInit () {
diff --git a/client/src/app/+accounts/accounts-routing.module.ts b/client/src/app/+accounts/accounts-routing.module.ts
index 531d763c4..55bce351a 100644
--- a/client/src/app/+accounts/accounts-routing.module.ts
+++ b/client/src/app/+accounts/accounts-routing.module.ts
@@ -14,7 +14,7 @@ const accountsRoutes: Routes = [
14 children: [ 14 children: [
15 { 15 {
16 path: '', 16 path: '',
17 redirectTo: 'videos', 17 redirectTo: 'video-channels',
18 pathMatch: 'full' 18 pathMatch: 'full'
19 }, 19 },
20 { 20 {
diff --git a/client/src/app/+accounts/accounts.component.html b/client/src/app/+accounts/accounts.component.html
index c1377c1ea..038e18c4b 100644
--- a/client/src/app/+accounts/accounts.component.html
+++ b/client/src/app/+accounts/accounts.component.html
@@ -26,10 +26,10 @@
26 </div> 26 </div>
27 27
28 <div class="links"> 28 <div class="links">
29 <a i18n routerLink="videos" routerLinkActive="active" class="title-page">Videos</a>
30
31 <a i18n routerLink="video-channels" routerLinkActive="active" class="title-page">Video channels</a> 29 <a i18n routerLink="video-channels" routerLinkActive="active" class="title-page">Video channels</a>
32 30
31 <a i18n routerLink="videos" routerLinkActive="active" class="title-page">Videos</a>
32
33 <a i18n routerLink="about" routerLinkActive="active" class="title-page">About</a> 33 <a i18n routerLink="about" routerLinkActive="active" class="title-page">About</a>
34 </div> 34 </div>
35 </div> 35 </div>
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 907aefae1..7990044a2 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
@@ -5,7 +5,7 @@ import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
5import { Subscription } from 'rxjs' 5import { Subscription } from 'rxjs'
6import { Notifier } from '@app/core' 6import { Notifier } from '@app/core'
7import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' 7import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
8import { ComponentPagination } from '@app/shared/rest/component-pagination.model' 8import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model'
9import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' 9import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
10 10
11@Component({ 11@Component({
@@ -46,8 +46,7 @@ export class VideoChannelPlaylistsComponent implements OnInit, OnDestroy {
46 } 46 }
47 47
48 onNearOfBottom () { 48 onNearOfBottom () {
49 // Last page 49 if (!hasMoreItems(this.pagination)) return
50 if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
51 50
52 this.pagination.currentPage += 1 51 this.pagination.currentPage += 1
53 this.loadVideoPlaylists() 52 this.loadVideoPlaylists()
diff --git a/client/src/app/shared/video-channel/video-channel.service.ts b/client/src/app/shared/video-channel/video-channel.service.ts
index d0bec649a..0168d37d9 100644
--- a/client/src/app/shared/video-channel/video-channel.service.ts
+++ b/client/src/app/shared/video-channel/video-channel.service.ts
@@ -2,7 +2,7 @@ import { catchError, map, tap } from 'rxjs/operators'
2import { Injectable } from '@angular/core' 2import { Injectable } from '@angular/core'
3import { Observable, ReplaySubject } from 'rxjs' 3import { Observable, ReplaySubject } from 'rxjs'
4import { RestExtractor } from '../rest/rest-extractor.service' 4import { RestExtractor } from '../rest/rest-extractor.service'
5import { HttpClient } from '@angular/common/http' 5import { HttpClient, HttpParams } from '@angular/common/http'
6import { VideoChannel as VideoChannelServer, VideoChannelCreate, VideoChannelUpdate } from '../../../../../shared/models/videos' 6import { VideoChannel as VideoChannelServer, VideoChannelCreate, VideoChannelUpdate } from '../../../../../shared/models/videos'
7import { AccountService } from '../account/account.service' 7import { AccountService } from '../account/account.service'
8import { ResultList } from '../../../../../shared' 8import { ResultList } from '../../../../../shared'
@@ -10,6 +10,8 @@ import { VideoChannel } from './video-channel.model'
10import { environment } from '../../../environments/environment' 10import { environment } from '../../../environments/environment'
11import { Account } from '@app/shared/account/account.model' 11import { Account } from '@app/shared/account/account.model'
12import { Avatar } from '../../../../../shared/models/avatars/avatar.model' 12import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
13import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
14import { RestService } from '@app/shared/rest'
13 15
14@Injectable() 16@Injectable()
15export class VideoChannelService { 17export class VideoChannelService {
@@ -29,6 +31,7 @@ export class VideoChannelService {
29 31
30 constructor ( 32 constructor (
31 private authHttp: HttpClient, 33 private authHttp: HttpClient,
34 private restService: RestService,
32 private restExtractor: RestExtractor 35 private restExtractor: RestExtractor
33 ) { } 36 ) { }
34 37
@@ -41,8 +44,16 @@ export class VideoChannelService {
41 ) 44 )
42 } 45 }
43 46
44 listAccountVideoChannels (account: Account): Observable<ResultList<VideoChannel>> { 47 listAccountVideoChannels (account: Account, componentPagination?: ComponentPagination): Observable<ResultList<VideoChannel>> {
45 return this.authHttp.get<ResultList<VideoChannelServer>>(AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/video-channels') 48 const pagination = componentPagination
49 ? this.restService.componentPaginationToRestPagination(componentPagination)
50 : { start: 0, count: 20 }
51
52 let params = new HttpParams()
53 params = this.restService.addRestGetParams(params, pagination)
54
55 const url = AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/video-channels'
56 return this.authHttp.get<ResultList<VideoChannelServer>>(url, { params })
46 .pipe( 57 .pipe(
47 map(res => VideoChannelService.extractVideoChannels(res)), 58 map(res => VideoChannelService.extractVideoChannels(res)),
48 catchError(err => this.restExtractor.handleError(err)) 59 catchError(err => this.restExtractor.handleError(err))
diff --git a/client/src/app/shared/video/abstract-video-list.html b/client/src/app/shared/video/abstract-video-list.html
index 268677977..14f48b54b 100644
--- a/client/src/app/shared/video/abstract-video-list.html
+++ b/client/src/app/shared/video/abstract-video-list.html
@@ -6,7 +6,7 @@
6 </div> 6 </div>
7 </div> 7 </div>
8 8
9 <my-feed [syndicationItems]="syndicationItems"></my-feed> 9 <my-feed *ngIf="titlePage" [syndicationItems]="syndicationItems"></my-feed>
10 10
11 <div class="moderation-block" *ngIf="displayModerationBlock"> 11 <div class="moderation-block" *ngIf="displayModerationBlock">
12 <my-peertube-checkbox 12 <my-peertube-checkbox
diff --git a/client/src/app/shared/video/abstract-video-list.scss b/client/src/app/shared/video/abstract-video-list.scss
index 9d481d6e4..d94596113 100644
--- a/client/src/app/shared/video/abstract-video-list.scss
+++ b/client/src/app/shared/video/abstract-video-list.scss
@@ -25,32 +25,6 @@
25} 25}
26 26
27.margin-content { 27.margin-content {
28 width: $video-miniature-width * 6; 28 @include adapt-margin-content-width;
29 margin: auto !important;
30
31 @media screen and (max-width: 1800px) {
32 width: $video-miniature-width * 5;
33 }
34
35 @media screen and (max-width: 1800px - $video-miniature-width) {
36 width: $video-miniature-width * 4;
37 }
38
39 @media screen and (max-width: 1800px - (2* $video-miniature-width)) {
40 width: $video-miniature-width * 3;
41 }
42
43 @media screen and (max-width: 1800px - (3* $video-miniature-width)) {
44 width: $video-miniature-width * 2;
45 }
46
47 @media screen and (max-width: 500px) {
48 width: auto;
49 margin: 0 !important;
50
51 .videos {
52 @include video-miniature-small-screen;
53 }
54 }
55} 29}
56 30
diff --git a/client/src/app/videos/video-list/video-overview.component.html b/client/src/app/videos/video-list/video-overview.component.html
index b644dd798..f59de584a 100644
--- a/client/src/app/videos/video-list/video-overview.component.html
+++ b/client/src/app/videos/video-list/video-overview.component.html
@@ -3,7 +3,7 @@
3 <div class="no-results" i18n *ngIf="notResults">No results.</div> 3 <div class="no-results" i18n *ngIf="notResults">No results.</div>
4 4
5 <div class="section" *ngFor="let object of overview.categories"> 5 <div class="section" *ngFor="let object of overview.categories">
6 <div class="section-title" i18n> 6 <div class="section-title">
7 <a routerLink="/search" [queryParams]="{ categoryOneOf: [ object.category.id ] }">{{ object.category.label }}</a> 7 <a routerLink="/search" [queryParams]="{ categoryOneOf: [ object.category.id ] }">{{ object.category.label }}</a>
8 </div> 8 </div>
9 9
@@ -11,7 +11,7 @@
11 </div> 11 </div>
12 12
13 <div class="section" *ngFor="let object of overview.tags"> 13 <div class="section" *ngFor="let object of overview.tags">
14 <div class="section-title" i18n> 14 <div class="section-title">
15 <a routerLink="/search" [queryParams]="{ tagsOneOf: [ object.tag ] }">#{{ object.tag }}</a> 15 <a routerLink="/search" [queryParams]="{ tagsOneOf: [ object.tag ] }">#{{ object.tag }}</a>
16 </div> 16 </div>
17 17
@@ -19,7 +19,7 @@
19 </div> 19 </div>
20 20
21 <div class="section channel" *ngFor="let object of overview.channels"> 21 <div class="section channel" *ngFor="let object of overview.channels">
22 <div class="section-title" i18n> 22 <div class="section-title">
23 <a [routerLink]="[ '/video-channels', buildVideoChannelBy(object) ]"> 23 <a [routerLink]="[ '/video-channels', buildVideoChannelBy(object) ]">
24 <img [src]="buildVideoChannelAvatarUrl(object)" alt="Avatar" /> 24 <img [src]="buildVideoChannelAvatarUrl(object)" alt="Avatar" />
25 25
diff --git a/client/src/app/videos/video-list/video-overview.component.scss b/client/src/app/videos/video-list/video-overview.component.scss
index a24766783..ade6f53b7 100644
--- a/client/src/app/videos/video-list/video-overview.component.scss
+++ b/client/src/app/videos/video-list/video-overview.component.scss
@@ -2,62 +2,10 @@
2@import '_mixins'; 2@import '_mixins';
3@import '_miniature'; 3@import '_miniature';
4 4
5.section { 5.margin-content {
6 max-height: 500px; // 2 rows max 6 @include adapt-margin-content-width;
7 overflow: hidden;
8 padding-top: 10px;
9
10 &:first-child {
11 padding-top: 30px;
12 }
13
14 my-video-miniature {
15 text-align: left;
16 }
17}
18
19.section-title {
20 font-size: 24px;
21 font-weight: $font-semibold;
22 margin-bottom: 10px;
23
24 a {
25 &:hover, &:focus:not(.focus-visible), &:active {
26 text-decoration: none;
27 outline: none;
28 }
29
30 color: var(--mainForegroundColor);
31 }
32} 7}
33 8
34.channel { 9.section {
35 .section-title a { 10 @include miniature-rows;
36 display: flex;
37 width: fit-content;
38 align-items: center;
39
40 img {
41 @include avatar(28px);
42
43 margin-right: 8px;
44 }
45 }
46}
47
48@media screen and (max-width: 500px) {
49 .margin-content {
50 margin: 0 !important;
51 }
52
53 .section-title {
54 font-size: 17px;
55 }
56
57 .section {
58 max-height: initial;
59 overflow: initial;
60
61 @include video-miniature-small-screen;
62 }
63} 11}
diff --git a/client/src/sass/include/_miniature.scss b/client/src/sass/include/_miniature.scss
index b62187fd2..3afcca310 100644
--- a/client/src/sass/include/_miniature.scss
+++ b/client/src/sass/include/_miniature.scss
@@ -138,3 +138,98 @@ $play-overlay-width: 18px;
138 } 138 }
139 } 139 }
140} 140}
141
142@mixin miniature-rows {
143 max-height: 540px; // 2 rows max
144 overflow: hidden;
145 padding-top: 10px;
146
147 &:first-child {
148 padding-top: 30px;
149 }
150
151 my-video-miniature {
152 text-align: left;
153 }
154
155 .section-title {
156 font-size: 24px;
157 font-weight: $font-semibold;
158 margin-bottom: 30px;
159
160 a {
161 &:hover, &:focus:not(.focus-visible), &:active {
162 text-decoration: none;
163 outline: none;
164 }
165
166 color: var(--mainForegroundColor);
167 }
168 }
169
170 &.channel {
171 .section-title {
172 a {
173 display: flex;
174 width: fit-content;
175 align-items: center;
176
177 img {
178 @include avatar(28px);
179
180 margin-right: 8px;
181 }
182 }
183
184 .followers {
185 color: $grey-foreground-color;
186 font-weight: normal;
187 font-size: 14px;
188 margin-left: 10px;
189 position: relative;
190 top: 2px;
191 }
192 }
193 }
194
195 @media screen and (max-width: $mobile-view) {
196 max-height: initial;
197 overflow: initial;
198
199 @include video-miniature-small-screen;
200
201 .section-title {
202 font-size: 17px;
203 }
204 }
205}
206
207@mixin adapt-margin-content-width {
208 width: $video-miniature-width * 6;
209 margin: auto !important;
210
211 @media screen and (max-width: 1800px) {
212 width: $video-miniature-width * 5;
213 }
214
215 @media screen and (max-width: 1800px - $video-miniature-width) {
216 width: $video-miniature-width * 4;
217 }
218
219 @media screen and (max-width: 1800px - (2* $video-miniature-width)) {
220 width: $video-miniature-width * 3;
221 }
222
223 @media screen and (max-width: 1800px - (3* $video-miniature-width)) {
224 width: $video-miniature-width * 2;
225 }
226
227 @media screen and (max-width: 500px) {
228 width: auto;
229 margin: 0 !important;
230
231 .videos {
232 @include video-miniature-small-screen;
233 }
234 }
235}