Infinite scroll to list our subscriptions
This commit is contained in:
parent
f37dc0dd14
commit
aa55a4da42
10 changed files with 67 additions and 67 deletions
|
@ -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">
|
<div *ngFor="let videoChannel of videoChannels" class="video-channel">
|
||||||
<a [routerLink]="[ '/video-channels', videoChannel.name ]">
|
<a [routerLink]="[ '/video-channels', videoChannel.name ]">
|
||||||
<img [src]="videoChannel.avatarUrl" alt="Avatar" />
|
<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 { VideoChannel } from '@app/shared/video-channel/video-channel.model'
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { UserSubscriptionService } from '@app/shared/user-subscription'
|
import { UserSubscriptionService } from '@app/shared/user-subscription'
|
||||||
|
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-account-subscriptions',
|
selector: 'my-account-subscriptions',
|
||||||
|
@ -12,6 +13,12 @@ import { UserSubscriptionService } from '@app/shared/user-subscription'
|
||||||
export class MyAccountSubscriptionsComponent implements OnInit {
|
export class MyAccountSubscriptionsComponent implements OnInit {
|
||||||
videoChannels: VideoChannel[] = []
|
videoChannels: VideoChannel[] = []
|
||||||
|
|
||||||
|
pagination: ComponentPagination = {
|
||||||
|
currentPage: 1,
|
||||||
|
itemsPerPage: 10,
|
||||||
|
totalItems: null
|
||||||
|
}
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private userSubscriptionService: UserSubscriptionService,
|
private userSubscriptionService: UserSubscriptionService,
|
||||||
private notificationsService: NotificationsService,
|
private notificationsService: NotificationsService,
|
||||||
|
@ -19,12 +26,27 @@ export class MyAccountSubscriptionsComponent implements OnInit {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.userSubscriptionService.listSubscriptions()
|
this.loadSubscriptions()
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSubscriptions () {
|
||||||
|
this.userSubscriptionService.listSubscriptions(this.pagination)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
res => this.videoChannels = res.data,
|
res => {
|
||||||
|
this.videoChannels = this.videoChannels.concat(res.data)
|
||||||
|
this.pagination.totalItems = res.total
|
||||||
|
},
|
||||||
|
|
||||||
error => this.notificationsService.error(this.i18n('Error'), error.message)
|
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>
|
||||||
|
|
||||||
<td *ngIf="isVideoImportPending(videoImport)">
|
<td *ngIf="isVideoImportPending(videoImport)">
|
||||||
{{ videoImport.video.name }}
|
{{ videoImport.video?.name }}
|
||||||
</td>
|
</td>
|
||||||
<td *ngIf="isVideoImportSuccess(videoImport)">
|
<td *ngIf="isVideoImportSuccess(videoImport) && videoImport.video">
|
||||||
<a [href]="getVideoUrl(videoImport.video)" target="_blank" rel="noopener noreferrer">{{ videoImport.video.name }}</a>
|
<a [href]="getVideoUrl(videoImport.video)" target="_blank" rel="noopener noreferrer">{{ videoImport.video?.name }}</a>
|
||||||
</td>
|
</td>
|
||||||
<td *ngIf="isVideoImportFailed(videoImport)"></td>
|
<td *ngIf="isVideoImportFailed(videoImport)"></td>
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
<td>{{ videoImport.createdAt }}</td>
|
<td>{{ videoImport.createdAt }}</td>
|
||||||
|
|
||||||
<td class="action-cell">
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</ng-template>
|
</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="results-header">
|
||||||
<div class="first-line">
|
<div class="first-line">
|
||||||
<div class="results-counter" *ngIf="pagination.totalItems">
|
<div class="results-counter" *ngIf="pagination.totalItems">
|
||||||
|
|
|
@ -87,9 +87,17 @@ export class SearchComponent implements OnInit, OnDestroy {
|
||||||
.subscribe(
|
.subscribe(
|
||||||
([ videosResult, videoChannelsResult ]) => {
|
([ videosResult, videoChannelsResult ]) => {
|
||||||
this.videos = this.videos.concat(videosResult.videos)
|
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 => {
|
error => {
|
||||||
|
@ -116,8 +124,10 @@ export class SearchComponent implements OnInit, OnDestroy {
|
||||||
private resetPagination () {
|
private resetPagination () {
|
||||||
this.pagination.currentPage = 1
|
this.pagination.currentPage = 1
|
||||||
this.pagination.totalItems = null
|
this.pagination.totalItems = null
|
||||||
|
this.channelsPerPage = 2
|
||||||
|
|
||||||
this.videos = []
|
this.videos = []
|
||||||
|
this.videoChannels = []
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateTitle () {
|
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 { HttpClient, HttpParams } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { ResultList } from '../../../../../shared'
|
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 { VideoChannel } from '@app/shared/video-channel/video-channel.model'
|
||||||
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
|
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
|
||||||
import { VideoChannel as VideoChannelServer } from '../../../../../shared/models/videos'
|
import { VideoChannel as VideoChannelServer } from '../../../../../shared/models/videos'
|
||||||
|
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
|
||||||
|
|
||||||
type SubscriptionExistResult = { [ uri: string ]: boolean }
|
type SubscriptionExistResult = { [ uri: string ]: boolean }
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ export class UserSubscriptionService {
|
||||||
|
|
||||||
// Use a replay subject because we "next" a value before subscribing
|
// Use a replay subject because we "next" a value before subscribing
|
||||||
private existsSubject: Subject<string> = new ReplaySubject(1)
|
private existsSubject: Subject<string> = new ReplaySubject(1)
|
||||||
private existsObservable: Observable<SubscriptionExistResult>
|
private readonly existsObservable: Observable<SubscriptionExistResult>
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private authHttp: HttpClient,
|
private authHttp: HttpClient,
|
||||||
|
@ -25,7 +26,6 @@ export class UserSubscriptionService {
|
||||||
private restService: RestService
|
private restService: RestService
|
||||||
) {
|
) {
|
||||||
this.existsObservable = this.existsSubject.pipe(
|
this.existsObservable = this.existsSubject.pipe(
|
||||||
tap(u => console.log(u)),
|
|
||||||
bufferTime(500),
|
bufferTime(500),
|
||||||
filter(uris => uris.length !== 0),
|
filter(uris => uris.length !== 0),
|
||||||
switchMap(uris => this.areSubscriptionExist(uris)),
|
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
|
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(
|
.pipe(
|
||||||
map(res => VideoChannelService.extractVideoChannels(res)),
|
map(res => VideoChannelService.extractVideoChannels(res)),
|
||||||
catchError(err => this.restExtractor.handleError(err))
|
catchError(err => this.restExtractor.handleError(err))
|
||||||
|
@ -67,11 +72,10 @@ export class UserSubscriptionService {
|
||||||
isSubscriptionExists (nameWithHost: string) {
|
isSubscriptionExists (nameWithHost: string) {
|
||||||
this.existsSubject.next(nameWithHost)
|
this.existsSubject.next(nameWithHost)
|
||||||
|
|
||||||
return this.existsObservable
|
return this.existsObservable.pipe(first())
|
||||||
}
|
}
|
||||||
|
|
||||||
private areSubscriptionExist (uris: string[]): Observable<SubscriptionExistResult> {
|
private areSubscriptionExist (uris: string[]): Observable<SubscriptionExistResult> {
|
||||||
console.log(uris)
|
|
||||||
const url = UserSubscriptionService.BASE_USER_SUBSCRIPTIONS_URL + '/exist'
|
const url = UserSubscriptionService.BASE_USER_SUBSCRIPTIONS_URL + '/exist'
|
||||||
let params = new HttpParams()
|
let params = new HttpParams()
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
|
||||||
@Input() containerHeight: number
|
@Input() containerHeight: number
|
||||||
@Input() pageHeight: number
|
@Input() pageHeight: number
|
||||||
@Input() percentLimit = 70
|
@Input() percentLimit = 70
|
||||||
@Input() autoLoading = false
|
@Input() autoInit = false
|
||||||
|
|
||||||
@Output() nearOfBottom = new EventEmitter<void>()
|
@Output() nearOfBottom = new EventEmitter<void>()
|
||||||
@Output() nearOfTop = new EventEmitter<void>()
|
@Output() nearOfTop = new EventEmitter<void>()
|
||||||
|
@ -29,7 +29,7 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
if (this.autoLoading === true) return this.initialize()
|
if (this.autoInit === true) return this.initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy () {
|
ngOnDestroy () {
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<div
|
<div
|
||||||
class="comment-threads"
|
class="comment-threads"
|
||||||
myInfiniteScroller
|
myInfiniteScroller
|
||||||
[autoLoading]="true"
|
[autoInit]="true"
|
||||||
(nearOfBottom)="onNearOfBottom()"
|
(nearOfBottom)="onNearOfBottom()"
|
||||||
>
|
>
|
||||||
<div #commentHighlightBlock id="highlighted-comment">
|
<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) {
|
static loadByActorAndTargetNameAndHostForAPI (actorId: number, targetName: string, targetHost: string, t?: Sequelize.Transaction) {
|
||||||
const actorFollowingPartInclude: IIncludeOptions = {
|
const actorFollowingPartInclude: IIncludeOptions = {
|
||||||
attributes: {
|
|
||||||
exclude: unusedActorAttributesForAPI
|
|
||||||
},
|
|
||||||
model: ActorModel,
|
model: ActorModel,
|
||||||
required: true,
|
required: true,
|
||||||
as: 'ActorFollowing',
|
as: 'ActorFollowing',
|
||||||
|
@ -203,7 +200,12 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
|
||||||
actorId
|
actorId
|
||||||
},
|
},
|
||||||
include: [
|
include: [
|
||||||
actorFollowingPartInclude
|
actorFollowingPartInclude,
|
||||||
|
{
|
||||||
|
model: ActorModel,
|
||||||
|
required: true,
|
||||||
|
as: 'ActorFollower'
|
||||||
|
}
|
||||||
],
|
],
|
||||||
transaction: t
|
transaction: t
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,8 @@ export const unusedActorAttributesForAPI = [
|
||||||
'outboxUrl',
|
'outboxUrl',
|
||||||
'sharedInboxUrl',
|
'sharedInboxUrl',
|
||||||
'followersUrl',
|
'followersUrl',
|
||||||
'followingUrl'
|
'followingUrl',
|
||||||
|
'url'
|
||||||
]
|
]
|
||||||
|
|
||||||
@DefaultScope({
|
@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 () {
|
toFormattedJSON () {
|
||||||
let avatar: Avatar = null
|
let avatar: Avatar = null
|
||||||
if (this.Avatar) {
|
if (this.Avatar) {
|
||||||
|
|
Loading…
Reference in a new issue