mirror of https://github.com/Chocobozzz/PeerTube
Infinite scroll to list our subscriptions
parent
f37dc0dd14
commit
aa55a4da42
|
@ -1,4 +1,4 @@
|
|||
<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" />
|
||||
|
|
|
@ -3,6 +3,7 @@ import { NotificationsService } from 'angular2-notifications'
|
|||
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',
|
||||
|
@ -12,6 +13,12 @@ import { UserSubscriptionService } from '@app/shared/user-subscription'
|
|||
export class MyAccountSubscriptionsComponent implements OnInit {
|
||||
videoChannels: VideoChannel[] = []
|
||||
|
||||
pagination: ComponentPagination = {
|
||||
currentPage: 1,
|
||||
itemsPerPage: 10,
|
||||
totalItems: null
|
||||
}
|
||||
|
||||
constructor (
|
||||
private userSubscriptionService: UserSubscriptionService,
|
||||
private notificationsService: NotificationsService,
|
||||
|
@ -19,12 +26,27 @@ export class MyAccountSubscriptionsComponent implements OnInit {
|
|||
) {}
|
||||
|
||||
ngOnInit () {
|
||||
this.userSubscriptionService.listSubscriptions()
|
||||
.subscribe(
|
||||
res => this.videoChannels = res.data,
|
||||
this.loadSubscriptions()
|
||||
}
|
||||
|
||||
error => this.notificationsService.error(this.i18n('Error'), error.message)
|
||||
)
|
||||
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
|
||||
|
||||
this.pagination.currentPage += 1
|
||||
this.loadSubscriptions()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,10 +29,10 @@
|
|||
</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>
|
||||
|
||||
|
@ -40,7 +40,7 @@
|
|||
<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>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<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">
|
||||
|
|
|
@ -87,9 +87,17 @@ export class SearchComponent implements OnInit, OnDestroy {
|
|||
.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 => {
|
||||
|
@ -116,8 +124,10 @@ export class SearchComponent implements OnInit, OnDestroy {
|
|||
private resetPagination () {
|
||||
this.pagination.currentPage = 1
|
||||
this.pagination.totalItems = null
|
||||
this.channelsPerPage = 2
|
||||
|
||||
this.videos = []
|
||||
this.videoChannels = []
|
||||
}
|
||||
|
||||
private updateTitle () {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
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'
|
||||
|
@ -8,6 +8,7 @@ import { Observable, ReplaySubject, Subject } from 'rxjs'
|
|||
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 }
|
||||
|
||||
|
@ -17,7 +18,7 @@ export class UserSubscriptionService {
|
|||
|
||||
// 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,
|
||||
|
@ -25,7 +26,6 @@ export class UserSubscriptionService {
|
|||
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)),
|
||||
|
@ -54,10 +54,15 @@ export class UserSubscriptionService {
|
|||
)
|
||||
}
|
||||
|
||||
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))
|
||||
|
@ -67,11 +72,10 @@ export class UserSubscriptionService {
|
|||
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()
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
|
|||
@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>()
|
||||
|
@ -29,7 +29,7 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
ngOnInit () {
|
||||
if (this.autoLoading === true) return this.initialize()
|
||||
if (this.autoInit === true) return this.initialize()
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<div
|
||||
class="comment-threads"
|
||||
myInfiniteScroller
|
||||
[autoLoading]="true"
|
||||
[autoInit]="true"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
>
|
||||
<div #commentHighlightBlock id="highlighted-comment">
|
||||
|
|
|
@ -169,9 +169,6 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
|
|||
|
||||
static loadByActorAndTargetNameAndHostForAPI (actorId: number, targetName: string, targetHost: string, t?: Sequelize.Transaction) {
|
||||
const actorFollowingPartInclude: IIncludeOptions = {
|
||||
attributes: {
|
||||
exclude: unusedActorAttributesForAPI
|
||||
},
|
||||
model: ActorModel,
|
||||
required: true,
|
||||
as: 'ActorFollowing',
|
||||
|
@ -203,7 +200,12 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
|
|||
actorId
|
||||
},
|
||||
include: [
|
||||
actorFollowingPartInclude
|
||||
actorFollowingPartInclude,
|
||||
{
|
||||
model: ActorModel,
|
||||
required: true,
|
||||
as: 'ActorFollower'
|
||||
}
|
||||
],
|
||||
transaction: t
|
||||
}
|
||||
|
|
|
@ -49,7 +49,8 @@ export const unusedActorAttributesForAPI = [
|
|||
'outboxUrl',
|
||||
'sharedInboxUrl',
|
||||
'followersUrl',
|
||||
'followingUrl'
|
||||
'followingUrl',
|
||||
'url'
|
||||
]
|
||||
|
||||
@DefaultScope({
|
||||
|
@ -322,45 +323,6 @@ export class ActorModel extends Model<ActorModel> {
|
|||
})
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
Loading…
Reference in New Issue