-<div class="video-channels">
+<div class="video-channels" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()">
<div *ngFor="let videoChannel of videoChannels" class="video-channel">
<a [routerLink]="[ '/video-channels', videoChannel.name ]">
<img [src]="videoChannel.avatarUrl" alt="Avatar" />
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { UserSubscriptionService } from '@app/shared/user-subscription'
+import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
@Component({
selector: 'my-account-subscriptions',
export class MyAccountSubscriptionsComponent implements OnInit {
videoChannels: VideoChannel[] = []
+ pagination: ComponentPagination = {
+ currentPage: 1,
+ itemsPerPage: 10,
+ totalItems: null
+ }
+
constructor (
private userSubscriptionService: UserSubscriptionService,
private notificationsService: NotificationsService,
) {}
ngOnInit () {
- this.userSubscriptionService.listSubscriptions()
- .subscribe(
- res => this.videoChannels = res.data,
+ this.loadSubscriptions()
+ }
+
+ loadSubscriptions () {
+ this.userSubscriptionService.listSubscriptions(this.pagination)
+ .subscribe(
+ res => {
+ this.videoChannels = this.videoChannels.concat(res.data)
+ this.pagination.totalItems = res.total
+ },
+
+ error => this.notificationsService.error(this.i18n('Error'), error.message)
+ )
+ }
+
+ onNearOfBottom () {
+ // Last page
+ if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
- error => this.notificationsService.error(this.i18n('Error'), error.message)
- )
+ this.pagination.currentPage += 1
+ this.loadSubscriptions()
}
}
</td>
<td *ngIf="isVideoImportPending(videoImport)">
- {{ videoImport.video.name }}
+ {{ videoImport.video?.name }}
</td>
- <td *ngIf="isVideoImportSuccess(videoImport)">
- <a [href]="getVideoUrl(videoImport.video)" target="_blank" rel="noopener noreferrer">{{ videoImport.video.name }}</a>
+ <td *ngIf="isVideoImportSuccess(videoImport) && videoImport.video">
+ <a [href]="getVideoUrl(videoImport.video)" target="_blank" rel="noopener noreferrer">{{ videoImport.video?.name }}</a>
</td>
<td *ngIf="isVideoImportFailed(videoImport)"></td>
<td>{{ videoImport.createdAt }}</td>
<td class="action-cell">
- <my-edit-button *ngIf="isVideoImportSuccess(videoImport)" [routerLink]="getEditVideoUrl(videoImport.video)"></my-edit-button>
+ <my-edit-button *ngIf="isVideoImportSuccess(videoImport) && videoImport.video" [routerLink]="getEditVideoUrl(videoImport.video)"></my-edit-button>
</td>
</tr>
</ng-template>
-<div myInfiniteScroller [autoLoading]="true" (nearOfBottom)="onNearOfBottom()" class="search-result">
+<div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" class="search-result">
<div class="results-header">
<div class="first-line">
<div class="results-counter" *ngIf="pagination.totalItems">
.subscribe(
([ videosResult, videoChannelsResult ]) => {
this.videos = this.videos.concat(videosResult.videos)
- this.pagination.totalItems = videosResult.totalVideos
+ this.pagination.totalItems = videosResult.totalVideos + videoChannelsResult.total
- this.videoChannels = videoChannelsResult.data
+ this.videoChannels = this.videoChannels.concat(videoChannelsResult.data)
+
+ // Focus on channels
+ if (this.channelsPerPage !== 10 && this.videos.length < this.pagination.itemsPerPage) {
+ this.resetPagination()
+
+ this.channelsPerPage = 10
+ this.search()
+ }
},
error => {
private resetPagination () {
this.pagination.currentPage = 1
this.pagination.totalItems = null
+ this.channelsPerPage = 2
this.videos = []
+ this.videoChannels = []
}
private updateTitle () {
-import { bufferTime, catchError, filter, map, share, switchMap, tap } from 'rxjs/operators'
+import { bufferTime, catchError, filter, first, map, share, switchMap } from 'rxjs/operators'
import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { ResultList } from '../../../../../shared'
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
import { VideoChannel as VideoChannelServer } from '../../../../../shared/models/videos'
+import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
type SubscriptionExistResult = { [ uri: string ]: boolean }
// Use a replay subject because we "next" a value before subscribing
private existsSubject: Subject<string> = new ReplaySubject(1)
- private existsObservable: Observable<SubscriptionExistResult>
+ private readonly existsObservable: Observable<SubscriptionExistResult>
constructor (
private authHttp: HttpClient,
private restService: RestService
) {
this.existsObservable = this.existsSubject.pipe(
- tap(u => console.log(u)),
bufferTime(500),
filter(uris => uris.length !== 0),
switchMap(uris => this.areSubscriptionExist(uris)),
)
}
- listSubscriptions (): Observable<ResultList<VideoChannel>> {
+ listSubscriptions (componentPagination: ComponentPagination): Observable<ResultList<VideoChannel>> {
const url = UserSubscriptionService.BASE_USER_SUBSCRIPTIONS_URL
- return this.authHttp.get<ResultList<VideoChannelServer>>(url)
+ const pagination = this.restService.componentPaginationToRestPagination(componentPagination)
+
+ let params = new HttpParams()
+ params = this.restService.addRestGetParams(params, pagination)
+
+ return this.authHttp.get<ResultList<VideoChannelServer>>(url, { params })
.pipe(
map(res => VideoChannelService.extractVideoChannels(res)),
catchError(err => this.restExtractor.handleError(err))
isSubscriptionExists (nameWithHost: string) {
this.existsSubject.next(nameWithHost)
- return this.existsObservable
+ return this.existsObservable.pipe(first())
}
private areSubscriptionExist (uris: string[]): Observable<SubscriptionExistResult> {
- console.log(uris)
const url = UserSubscriptionService.BASE_USER_SUBSCRIPTIONS_URL + '/exist'
let params = new HttpParams()
@Input() containerHeight: number
@Input() pageHeight: number
@Input() percentLimit = 70
- @Input() autoLoading = false
+ @Input() autoInit = false
@Output() nearOfBottom = new EventEmitter<void>()
@Output() nearOfTop = new EventEmitter<void>()
}
ngOnInit () {
- if (this.autoLoading === true) return this.initialize()
+ if (this.autoInit === true) return this.initialize()
}
ngOnDestroy () {
<div
class="comment-threads"
myInfiniteScroller
- [autoLoading]="true"
+ [autoInit]="true"
(nearOfBottom)="onNearOfBottom()"
>
<div #commentHighlightBlock id="highlighted-comment">
static loadByActorAndTargetNameAndHostForAPI (actorId: number, targetName: string, targetHost: string, t?: Sequelize.Transaction) {
const actorFollowingPartInclude: IIncludeOptions = {
- attributes: {
- exclude: unusedActorAttributesForAPI
- },
model: ActorModel,
required: true,
as: 'ActorFollowing',
actorId
},
include: [
- actorFollowingPartInclude
+ actorFollowingPartInclude,
+ {
+ model: ActorModel,
+ required: true,
+ as: 'ActorFollower'
+ }
],
transaction: t
}
'outboxUrl',
'sharedInboxUrl',
'followersUrl',
- 'followingUrl'
+ 'followingUrl',
+ 'url'
]
@DefaultScope({
})
}
- static async getActorsFollowerSharedInboxUrls (actors: ActorModel[], t: Sequelize.Transaction) {
- const query = {
- // attribute: [],
- where: {
- id: {
- [Sequelize.Op.in]: actors.map(a => a.id)
- }
- },
- include: [
- {
- // attributes: [ ],
- model: ActorFollowModel.unscoped(),
- required: true,
- as: 'ActorFollowers',
- where: {
- state: 'accepted'
- },
- include: [
- {
- attributes: [ 'sharedInboxUrl' ],
- model: ActorModel.unscoped(),
- as: 'ActorFollower',
- required: true
- }
- ]
- }
- ],
- transaction: t
- }
-
- const hash: { [ id: number ]: string[] } = {}
- const res = await ActorModel.findAll(query)
- for (const actor of res) {
- hash[actor.id] = actor.ActorFollowers.map(follow => follow.ActorFollower.sharedInboxUrl)
- }
-
- return hash
- }
-
toFormattedJSON () {
let avatar: Avatar = null
if (this.Avatar) {