diff options
author | Chocobozzz <me@florianbigard.com> | 2018-08-24 10:31:56 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-08-27 09:41:54 +0200 |
commit | aa55a4da422330fe2816f1764b64f6607a0ca4aa (patch) | |
tree | 39933a835cc13a685696178e374fe3ac8ba9003b | |
parent | f37dc0dd14d9ce0b59c454c2c1b935fcbe9727e9 (diff) | |
download | PeerTube-aa55a4da422330fe2816f1764b64f6607a0ca4aa.tar.gz PeerTube-aa55a4da422330fe2816f1764b64f6607a0ca4aa.tar.zst PeerTube-aa55a4da422330fe2816f1764b64f6607a0ca4aa.zip |
Infinite scroll to list our subscriptions
10 files changed, 67 insertions, 67 deletions
diff --git a/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.html b/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.html index 4c68cd1a5..3752de49f 100644 --- a/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.html +++ b/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.html | |||
@@ -1,4 +1,4 @@ | |||
1 | <div class="video-channels"> | 1 | <div class="video-channels" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()"> |
2 | <div *ngFor="let videoChannel of videoChannels" class="video-channel"> | 2 | <div *ngFor="let videoChannel of videoChannels" class="video-channel"> |
3 | <a [routerLink]="[ '/video-channels', videoChannel.name ]"> | 3 | <a [routerLink]="[ '/video-channels', videoChannel.name ]"> |
4 | <img [src]="videoChannel.avatarUrl" alt="Avatar" /> | 4 | <img [src]="videoChannel.avatarUrl" alt="Avatar" /> |
diff --git a/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.ts b/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.ts index 9434b196f..9517a3705 100644 --- a/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.ts +++ b/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.ts | |||
@@ -3,6 +3,7 @@ import { NotificationsService } from 'angular2-notifications' | |||
3 | import { VideoChannel } from '@app/shared/video-channel/video-channel.model' | 3 | import { VideoChannel } from '@app/shared/video-channel/video-channel.model' |
4 | import { I18n } from '@ngx-translate/i18n-polyfill' | 4 | import { I18n } from '@ngx-translate/i18n-polyfill' |
5 | import { UserSubscriptionService } from '@app/shared/user-subscription' | 5 | import { UserSubscriptionService } from '@app/shared/user-subscription' |
6 | import { ComponentPagination } from '@app/shared/rest/component-pagination.model' | ||
6 | 7 | ||
7 | @Component({ | 8 | @Component({ |
8 | selector: 'my-account-subscriptions', | 9 | selector: 'my-account-subscriptions', |
@@ -12,6 +13,12 @@ import { UserSubscriptionService } from '@app/shared/user-subscription' | |||
12 | export class MyAccountSubscriptionsComponent implements OnInit { | 13 | export class MyAccountSubscriptionsComponent implements OnInit { |
13 | videoChannels: VideoChannel[] = [] | 14 | videoChannels: VideoChannel[] = [] |
14 | 15 | ||
16 | pagination: ComponentPagination = { | ||
17 | currentPage: 1, | ||
18 | itemsPerPage: 10, | ||
19 | totalItems: null | ||
20 | } | ||
21 | |||
15 | constructor ( | 22 | constructor ( |
16 | private userSubscriptionService: UserSubscriptionService, | 23 | private userSubscriptionService: UserSubscriptionService, |
17 | private notificationsService: NotificationsService, | 24 | private notificationsService: NotificationsService, |
@@ -19,12 +26,27 @@ export class MyAccountSubscriptionsComponent implements OnInit { | |||
19 | ) {} | 26 | ) {} |
20 | 27 | ||
21 | ngOnInit () { | 28 | ngOnInit () { |
22 | this.userSubscriptionService.listSubscriptions() | 29 | this.loadSubscriptions() |
23 | .subscribe( | 30 | } |
24 | res => this.videoChannels = res.data, | 31 | |
32 | loadSubscriptions () { | ||
33 | this.userSubscriptionService.listSubscriptions(this.pagination) | ||
34 | .subscribe( | ||
35 | res => { | ||
36 | this.videoChannels = this.videoChannels.concat(res.data) | ||
37 | this.pagination.totalItems = res.total | ||
38 | }, | ||
39 | |||
40 | error => this.notificationsService.error(this.i18n('Error'), error.message) | ||
41 | ) | ||
42 | } | ||
43 | |||
44 | onNearOfBottom () { | ||
45 | // Last page | ||
46 | if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return | ||
25 | 47 | ||
26 | error => this.notificationsService.error(this.i18n('Error'), error.message) | 48 | this.pagination.currentPage += 1 |
27 | ) | 49 | this.loadSubscriptions() |
28 | } | 50 | } |
29 | 51 | ||
30 | } | 52 | } |
diff --git a/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.html b/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.html index b2b6c3d60..329948cb5 100644 --- a/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.html +++ b/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.html | |||
@@ -29,10 +29,10 @@ | |||
29 | </td> | 29 | </td> |
30 | 30 | ||
31 | <td *ngIf="isVideoImportPending(videoImport)"> | 31 | <td *ngIf="isVideoImportPending(videoImport)"> |
32 | {{ videoImport.video.name }} | 32 | {{ videoImport.video?.name }} |
33 | </td> | 33 | </td> |
34 | <td *ngIf="isVideoImportSuccess(videoImport)"> | 34 | <td *ngIf="isVideoImportSuccess(videoImport) && videoImport.video"> |
35 | <a [href]="getVideoUrl(videoImport.video)" target="_blank" rel="noopener noreferrer">{{ videoImport.video.name }}</a> | 35 | <a [href]="getVideoUrl(videoImport.video)" target="_blank" rel="noopener noreferrer">{{ videoImport.video?.name }}</a> |
36 | </td> | 36 | </td> |
37 | <td *ngIf="isVideoImportFailed(videoImport)"></td> | 37 | <td *ngIf="isVideoImportFailed(videoImport)"></td> |
38 | 38 | ||
@@ -40,7 +40,7 @@ | |||
40 | <td>{{ videoImport.createdAt }}</td> | 40 | <td>{{ videoImport.createdAt }}</td> |
41 | 41 | ||
42 | <td class="action-cell"> | 42 | <td class="action-cell"> |
43 | <my-edit-button *ngIf="isVideoImportSuccess(videoImport)" [routerLink]="getEditVideoUrl(videoImport.video)"></my-edit-button> | 43 | <my-edit-button *ngIf="isVideoImportSuccess(videoImport) && videoImport.video" [routerLink]="getEditVideoUrl(videoImport.video)"></my-edit-button> |
44 | </td> | 44 | </td> |
45 | </tr> | 45 | </tr> |
46 | </ng-template> | 46 | </ng-template> |
diff --git a/client/src/app/search/search.component.html b/client/src/app/search/search.component.html index 128cc52f5..83d014987 100644 --- a/client/src/app/search/search.component.html +++ b/client/src/app/search/search.component.html | |||
@@ -1,4 +1,4 @@ | |||
1 | <div myInfiniteScroller [autoLoading]="true" (nearOfBottom)="onNearOfBottom()" class="search-result"> | 1 | <div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" class="search-result"> |
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"> |
diff --git a/client/src/app/search/search.component.ts b/client/src/app/search/search.component.ts index f88df6391..ed84e24d9 100644 --- a/client/src/app/search/search.component.ts +++ b/client/src/app/search/search.component.ts | |||
@@ -87,9 +87,17 @@ export class SearchComponent implements OnInit, OnDestroy { | |||
87 | .subscribe( | 87 | .subscribe( |
88 | ([ videosResult, videoChannelsResult ]) => { | 88 | ([ videosResult, videoChannelsResult ]) => { |
89 | this.videos = this.videos.concat(videosResult.videos) | 89 | this.videos = this.videos.concat(videosResult.videos) |
90 | this.pagination.totalItems = videosResult.totalVideos | 90 | this.pagination.totalItems = videosResult.totalVideos + videoChannelsResult.total |
91 | 91 | ||
92 | this.videoChannels = videoChannelsResult.data | 92 | this.videoChannels = this.videoChannels.concat(videoChannelsResult.data) |
93 | |||
94 | // Focus on channels | ||
95 | if (this.channelsPerPage !== 10 && this.videos.length < this.pagination.itemsPerPage) { | ||
96 | this.resetPagination() | ||
97 | |||
98 | this.channelsPerPage = 10 | ||
99 | this.search() | ||
100 | } | ||
93 | }, | 101 | }, |
94 | 102 | ||
95 | error => { | 103 | error => { |
@@ -116,8 +124,10 @@ export class SearchComponent implements OnInit, OnDestroy { | |||
116 | private resetPagination () { | 124 | private resetPagination () { |
117 | this.pagination.currentPage = 1 | 125 | this.pagination.currentPage = 1 |
118 | this.pagination.totalItems = null | 126 | this.pagination.totalItems = null |
127 | this.channelsPerPage = 2 | ||
119 | 128 | ||
120 | this.videos = [] | 129 | this.videos = [] |
130 | this.videoChannels = [] | ||
121 | } | 131 | } |
122 | 132 | ||
123 | private updateTitle () { | 133 | private updateTitle () { |
diff --git a/client/src/app/shared/user-subscription/user-subscription.service.ts b/client/src/app/shared/user-subscription/user-subscription.service.ts index cf622019f..3d05f071e 100644 --- a/client/src/app/shared/user-subscription/user-subscription.service.ts +++ b/client/src/app/shared/user-subscription/user-subscription.service.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { bufferTime, catchError, filter, map, share, switchMap, tap } from 'rxjs/operators' | 1 | import { bufferTime, catchError, filter, first, map, share, switchMap } from 'rxjs/operators' |
2 | import { HttpClient, HttpParams } from '@angular/common/http' | 2 | import { HttpClient, HttpParams } from '@angular/common/http' |
3 | import { Injectable } from '@angular/core' | 3 | import { Injectable } from '@angular/core' |
4 | import { ResultList } from '../../../../../shared' | 4 | import { ResultList } from '../../../../../shared' |
@@ -8,6 +8,7 @@ import { Observable, ReplaySubject, Subject } from 'rxjs' | |||
8 | import { VideoChannel } from '@app/shared/video-channel/video-channel.model' | 8 | import { VideoChannel } from '@app/shared/video-channel/video-channel.model' |
9 | import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' | 9 | import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' |
10 | import { VideoChannel as VideoChannelServer } from '../../../../../shared/models/videos' | 10 | import { VideoChannel as VideoChannelServer } from '../../../../../shared/models/videos' |
11 | import { ComponentPagination } from '@app/shared/rest/component-pagination.model' | ||
11 | 12 | ||
12 | type SubscriptionExistResult = { [ uri: string ]: boolean } | 13 | type SubscriptionExistResult = { [ uri: string ]: boolean } |
13 | 14 | ||
@@ -17,7 +18,7 @@ export class UserSubscriptionService { | |||
17 | 18 | ||
18 | // Use a replay subject because we "next" a value before subscribing | 19 | // Use a replay subject because we "next" a value before subscribing |
19 | private existsSubject: Subject<string> = new ReplaySubject(1) | 20 | private existsSubject: Subject<string> = new ReplaySubject(1) |
20 | private existsObservable: Observable<SubscriptionExistResult> | 21 | private readonly existsObservable: Observable<SubscriptionExistResult> |
21 | 22 | ||
22 | constructor ( | 23 | constructor ( |
23 | private authHttp: HttpClient, | 24 | private authHttp: HttpClient, |
@@ -25,7 +26,6 @@ export class UserSubscriptionService { | |||
25 | private restService: RestService | 26 | private restService: RestService |
26 | ) { | 27 | ) { |
27 | this.existsObservable = this.existsSubject.pipe( | 28 | this.existsObservable = this.existsSubject.pipe( |
28 | tap(u => console.log(u)), | ||
29 | bufferTime(500), | 29 | bufferTime(500), |
30 | filter(uris => uris.length !== 0), | 30 | filter(uris => uris.length !== 0), |
31 | switchMap(uris => this.areSubscriptionExist(uris)), | 31 | switchMap(uris => this.areSubscriptionExist(uris)), |
@@ -54,10 +54,15 @@ export class UserSubscriptionService { | |||
54 | ) | 54 | ) |
55 | } | 55 | } |
56 | 56 | ||
57 | listSubscriptions (): Observable<ResultList<VideoChannel>> { | 57 | listSubscriptions (componentPagination: ComponentPagination): Observable<ResultList<VideoChannel>> { |
58 | const url = UserSubscriptionService.BASE_USER_SUBSCRIPTIONS_URL | 58 | const url = UserSubscriptionService.BASE_USER_SUBSCRIPTIONS_URL |
59 | 59 | ||
60 | return this.authHttp.get<ResultList<VideoChannelServer>>(url) | 60 | const pagination = this.restService.componentPaginationToRestPagination(componentPagination) |
61 | |||
62 | let params = new HttpParams() | ||
63 | params = this.restService.addRestGetParams(params, pagination) | ||
64 | |||
65 | return this.authHttp.get<ResultList<VideoChannelServer>>(url, { params }) | ||
61 | .pipe( | 66 | .pipe( |
62 | map(res => VideoChannelService.extractVideoChannels(res)), | 67 | map(res => VideoChannelService.extractVideoChannels(res)), |
63 | catchError(err => this.restExtractor.handleError(err)) | 68 | catchError(err => this.restExtractor.handleError(err)) |
@@ -67,11 +72,10 @@ export class UserSubscriptionService { | |||
67 | isSubscriptionExists (nameWithHost: string) { | 72 | isSubscriptionExists (nameWithHost: string) { |
68 | this.existsSubject.next(nameWithHost) | 73 | this.existsSubject.next(nameWithHost) |
69 | 74 | ||
70 | return this.existsObservable | 75 | return this.existsObservable.pipe(first()) |
71 | } | 76 | } |
72 | 77 | ||
73 | private areSubscriptionExist (uris: string[]): Observable<SubscriptionExistResult> { | 78 | private areSubscriptionExist (uris: string[]): Observable<SubscriptionExistResult> { |
74 | console.log(uris) | ||
75 | const url = UserSubscriptionService.BASE_USER_SUBSCRIPTIONS_URL + '/exist' | 79 | const url = UserSubscriptionService.BASE_USER_SUBSCRIPTIONS_URL + '/exist' |
76 | let params = new HttpParams() | 80 | let params = new HttpParams() |
77 | 81 | ||
diff --git a/client/src/app/shared/video/infinite-scroller.directive.ts b/client/src/app/shared/video/infinite-scroller.directive.ts index 0448e2c23..4dc1f86e7 100644 --- a/client/src/app/shared/video/infinite-scroller.directive.ts +++ b/client/src/app/shared/video/infinite-scroller.directive.ts | |||
@@ -11,7 +11,7 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy { | |||
11 | @Input() containerHeight: number | 11 | @Input() containerHeight: number |
12 | @Input() pageHeight: number | 12 | @Input() pageHeight: number |
13 | @Input() percentLimit = 70 | 13 | @Input() percentLimit = 70 |
14 | @Input() autoLoading = false | 14 | @Input() autoInit = false |
15 | 15 | ||
16 | @Output() nearOfBottom = new EventEmitter<void>() | 16 | @Output() nearOfBottom = new EventEmitter<void>() |
17 | @Output() nearOfTop = new EventEmitter<void>() | 17 | @Output() nearOfTop = new EventEmitter<void>() |
@@ -29,7 +29,7 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy { | |||
29 | } | 29 | } |
30 | 30 | ||
31 | ngOnInit () { | 31 | ngOnInit () { |
32 | if (this.autoLoading === true) return this.initialize() | 32 | if (this.autoInit === true) return this.initialize() |
33 | } | 33 | } |
34 | 34 | ||
35 | ngOnDestroy () { | 35 | ngOnDestroy () { |
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 8871980e9..bf6706ed3 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 | |||
@@ -21,7 +21,7 @@ | |||
21 | <div | 21 | <div |
22 | class="comment-threads" | 22 | class="comment-threads" |
23 | myInfiniteScroller | 23 | myInfiniteScroller |
24 | [autoLoading]="true" | 24 | [autoInit]="true" |
25 | (nearOfBottom)="onNearOfBottom()" | 25 | (nearOfBottom)="onNearOfBottom()" |
26 | > | 26 | > |
27 | <div #commentHighlightBlock id="highlighted-comment"> | 27 | <div #commentHighlightBlock id="highlighted-comment"> |
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/activitypub/actor-follow.ts index 81fcf7001..ebb2d47c2 100644 --- a/server/models/activitypub/actor-follow.ts +++ b/server/models/activitypub/actor-follow.ts | |||
@@ -169,9 +169,6 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
169 | 169 | ||
170 | static loadByActorAndTargetNameAndHostForAPI (actorId: number, targetName: string, targetHost: string, t?: Sequelize.Transaction) { | 170 | static loadByActorAndTargetNameAndHostForAPI (actorId: number, targetName: string, targetHost: string, t?: Sequelize.Transaction) { |
171 | const actorFollowingPartInclude: IIncludeOptions = { | 171 | const actorFollowingPartInclude: IIncludeOptions = { |
172 | attributes: { | ||
173 | exclude: unusedActorAttributesForAPI | ||
174 | }, | ||
175 | model: ActorModel, | 172 | model: ActorModel, |
176 | required: true, | 173 | required: true, |
177 | as: 'ActorFollowing', | 174 | as: 'ActorFollowing', |
@@ -203,7 +200,12 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
203 | actorId | 200 | actorId |
204 | }, | 201 | }, |
205 | include: [ | 202 | include: [ |
206 | actorFollowingPartInclude | 203 | actorFollowingPartInclude, |
204 | { | ||
205 | model: ActorModel, | ||
206 | required: true, | ||
207 | as: 'ActorFollower' | ||
208 | } | ||
207 | ], | 209 | ], |
208 | transaction: t | 210 | transaction: t |
209 | } | 211 | } |
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts index ec0b4b2d9..e16bd5d79 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/activitypub/actor.ts | |||
@@ -49,7 +49,8 @@ export const unusedActorAttributesForAPI = [ | |||
49 | 'outboxUrl', | 49 | 'outboxUrl', |
50 | 'sharedInboxUrl', | 50 | 'sharedInboxUrl', |
51 | 'followersUrl', | 51 | 'followersUrl', |
52 | 'followingUrl' | 52 | 'followingUrl', |
53 | 'url' | ||
53 | ] | 54 | ] |
54 | 55 | ||
55 | @DefaultScope({ | 56 | @DefaultScope({ |
@@ -322,45 +323,6 @@ export class ActorModel extends Model<ActorModel> { | |||
322 | }) | 323 | }) |
323 | } | 324 | } |
324 | 325 | ||
325 | static async getActorsFollowerSharedInboxUrls (actors: ActorModel[], t: Sequelize.Transaction) { | ||
326 | const query = { | ||
327 | // attribute: [], | ||
328 | where: { | ||
329 | id: { | ||
330 | [Sequelize.Op.in]: actors.map(a => a.id) | ||
331 | } | ||
332 | }, | ||
333 | include: [ | ||
334 | { | ||
335 | // attributes: [ ], | ||
336 | model: ActorFollowModel.unscoped(), | ||
337 | required: true, | ||
338 | as: 'ActorFollowers', | ||
339 | where: { | ||
340 | state: 'accepted' | ||
341 | }, | ||
342 | include: [ | ||
343 | { | ||
344 | attributes: [ 'sharedInboxUrl' ], | ||
345 | model: ActorModel.unscoped(), | ||
346 | as: 'ActorFollower', | ||
347 | required: true | ||
348 | } | ||
349 | ] | ||
350 | } | ||
351 | ], | ||
352 | transaction: t | ||
353 | } | ||
354 | |||
355 | const hash: { [ id: number ]: string[] } = {} | ||
356 | const res = await ActorModel.findAll(query) | ||
357 | for (const actor of res) { | ||
358 | hash[actor.id] = actor.ActorFollowers.map(follow => follow.ActorFollower.sharedInboxUrl) | ||
359 | } | ||
360 | |||
361 | return hash | ||
362 | } | ||
363 | |||
364 | toFormattedJSON () { | 326 | toFormattedJSON () { |
365 | let avatar: Avatar = null | 327 | let avatar: Avatar = null |
366 | if (this.Avatar) { | 328 | if (this.Avatar) { |