parent
52aba7a1cb
commit
a91fd0cb35
|
@ -4,7 +4,14 @@
|
|||
|
||||
<div class="no-results" i18n *ngIf="channelPagination.totalItems === 0">This account does not have channels.</div>
|
||||
|
||||
<div class="channels" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onChannelDataSubject.asObservable()">
|
||||
<my-infinite-scroller
|
||||
class="channels"
|
||||
[(currentPage)]="channelPagination.currentPage"
|
||||
[isLoading]="isLoading"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
(currentPageChange)="onPageChange()"
|
||||
[hasMore]="hasMoreVideoChannels"
|
||||
>
|
||||
<div class="channel" *ngFor="let videoChannel of videoChannels">
|
||||
|
||||
<div class="channel-avatar-row">
|
||||
|
@ -52,5 +59,5 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { from, Subject, Subscription } from 'rxjs'
|
||||
import { from, Subject } from 'rxjs'
|
||||
import { concatMap, map, switchMap, tap } from 'rxjs/operators'
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||
import { Component, OnInit } from '@angular/core'
|
||||
import { ComponentPagination, hasMoreItems, MarkdownService, User, UserService } from '@app/core'
|
||||
import { SimpleMemoize } from '@app/helpers'
|
||||
import { NSFWPolicyType, VideoSortField } from '@peertube/peertube-models'
|
||||
|
@ -8,7 +8,7 @@ import { MiniatureDisplayOptions, VideoMiniatureComponent } from '../../shared/s
|
|||
import { SubscribeButtonComponent } from '../../shared/shared-user-subscription/subscribe-button.component'
|
||||
import { RouterLink } from '@angular/router'
|
||||
import { ActorAvatarComponent } from '../../shared/shared-actor-image/actor-avatar.component'
|
||||
import { InfiniteScrollerDirective } from '../../shared/shared-main/common/infinite-scroller.directive'
|
||||
import { InfiniteScrollerComponent } from '../../shared/shared-main/common/infinite-scroller.component'
|
||||
import { NgIf, NgFor } from '@angular/common'
|
||||
import { AccountService } from '@app/shared/shared-main/account/account.service'
|
||||
import { VideoChannelService } from '@app/shared/shared-main/channel/video-channel.service'
|
||||
|
@ -22,14 +22,17 @@ import { Video } from '@app/shared/shared-main/video/video.model'
|
|||
templateUrl: './account-video-channels.component.html',
|
||||
styleUrls: [ './account-video-channels.component.scss' ],
|
||||
standalone: true,
|
||||
imports: [ NgIf, InfiniteScrollerDirective, NgFor, ActorAvatarComponent, RouterLink, SubscribeButtonComponent, VideoMiniatureComponent ]
|
||||
imports: [ NgIf, InfiniteScrollerComponent, NgFor, ActorAvatarComponent, RouterLink, SubscribeButtonComponent, VideoMiniatureComponent ]
|
||||
})
|
||||
export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
|
||||
export class AccountVideoChannelsComponent implements OnInit {
|
||||
account: Account
|
||||
videoChannels: VideoChannel[] = []
|
||||
|
||||
videos: { [id: number]: { total: number, videos: Video[] } } = {}
|
||||
|
||||
hasMoreVideoChannels = true
|
||||
isLoading = true
|
||||
|
||||
channelsDescriptionHTML: { [ id: number ]: string } = {}
|
||||
|
||||
channelPagination: ComponentPagination = {
|
||||
|
@ -60,8 +63,6 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
|
|||
blacklistInfo: false
|
||||
}
|
||||
|
||||
private accountSub: Subscription
|
||||
|
||||
constructor (
|
||||
private accountService: AccountService,
|
||||
private videoChannelService: VideoChannelService,
|
||||
|
@ -71,15 +72,6 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
|
|||
) { }
|
||||
|
||||
ngOnInit () {
|
||||
// Parent get the account for us
|
||||
this.accountSub = this.accountService.accountLoaded
|
||||
.subscribe(account => {
|
||||
this.account = account
|
||||
this.videoChannels = []
|
||||
|
||||
this.loadMoreChannels()
|
||||
})
|
||||
|
||||
this.userService.getAnonymousOrLoggedUser()
|
||||
.subscribe(user => {
|
||||
this.userMiniature = user
|
||||
|
@ -88,18 +80,22 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
|
|||
})
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
if (this.accountSub) this.accountSub.unsubscribe()
|
||||
}
|
||||
loadMoreChannels (reset = false) {
|
||||
let hasDoneReset = false
|
||||
this.isLoading = true
|
||||
|
||||
loadMoreChannels () {
|
||||
const options = {
|
||||
account: this.account,
|
||||
componentPagination: this.channelPagination,
|
||||
sort: '-updatedAt'
|
||||
}
|
||||
|
||||
this.videoChannelService.listAccountVideoChannels(options)
|
||||
// Parent get the account for us
|
||||
this.accountService.accountLoaded
|
||||
.pipe(
|
||||
tap(account => {
|
||||
this.account = account
|
||||
}),
|
||||
switchMap(() => this.videoChannelService.listAccountVideoChannels({
|
||||
account: this.account,
|
||||
componentPagination: this.channelPagination,
|
||||
sort: '-updatedAt'
|
||||
}))
|
||||
)
|
||||
.pipe(
|
||||
tap(res => {
|
||||
this.channelPagination.totalItems = res.total
|
||||
|
@ -118,13 +114,21 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
|
|||
})
|
||||
)
|
||||
.subscribe(async ({ videoChannel, videos, total }) => {
|
||||
this.isLoading = false
|
||||
this.channelsDescriptionHTML[videoChannel.id] = await this.markdown.textMarkdownToHTML({
|
||||
markdown: videoChannel.description,
|
||||
withEmoji: true,
|
||||
withHtml: true
|
||||
})
|
||||
|
||||
if (reset && !hasDoneReset) {
|
||||
hasDoneReset = true
|
||||
this.videoChannels = []
|
||||
}
|
||||
|
||||
this.videoChannels.push(videoChannel)
|
||||
this.hasMoreVideoChannels = (this.channelPagination.currentPage * this.channelPagination.itemsPerPage) <
|
||||
this.channelPagination.totalItems
|
||||
|
||||
this.videos[videoChannel.id] = { videos, total }
|
||||
|
||||
|
@ -150,6 +154,10 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
|
|||
return this.channelsDescriptionHTML[videoChannel.id]
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
this.loadMoreChannels(true)
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
if (!hasMoreItems(this.channelPagination)) return
|
||||
|
||||
|
|
|
@ -4,7 +4,14 @@
|
|||
{{ getNoResultMessage() }}
|
||||
</div>
|
||||
|
||||
<div class="plugins" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
|
||||
<my-infinite-scroller
|
||||
class="plugins"
|
||||
[(currentPage)]="pagination.currentPage"
|
||||
[isLoading]="isLoading"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
(currentPageChange)="onPageChange()"
|
||||
[hasMore]="hasMoreResults"
|
||||
>
|
||||
<ng-container *ngFor="let plugin of plugins">
|
||||
<my-plugin-card [plugin]="plugin" [version]="plugin.version" [pluginType]="pluginType">
|
||||
<div ngProjectAs="buttons">
|
||||
|
@ -27,4 +34,4 @@
|
|||
</div>
|
||||
</my-plugin-card>
|
||||
</ng-container>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Subject } from 'rxjs'
|
||||
import { distinct, filter, ReplaySubject } from 'rxjs'
|
||||
import { Component, OnInit } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
|
||||
|
@ -10,7 +10,7 @@ import { DeleteButtonComponent } from '../../../shared/shared-main/buttons/delet
|
|||
import { ButtonComponent } from '../../../shared/shared-main/buttons/button.component'
|
||||
import { EditButtonComponent } from '../../../shared/shared-main/buttons/edit-button.component'
|
||||
import { PluginCardComponent } from '../shared/plugin-card.component'
|
||||
import { InfiniteScrollerDirective } from '../../../shared/shared-main/common/infinite-scroller.directive'
|
||||
import { InfiniteScrollerComponent } from '../../../shared/shared-main/common/infinite-scroller.component'
|
||||
import { NgIf, NgFor } from '@angular/common'
|
||||
import { PluginNavigationComponent } from '../shared/plugin-navigation.component'
|
||||
|
||||
|
@ -22,7 +22,7 @@ import { PluginNavigationComponent } from '../shared/plugin-navigation.component
|
|||
imports: [
|
||||
PluginNavigationComponent,
|
||||
NgIf,
|
||||
InfiniteScrollerDirective,
|
||||
InfiniteScrollerComponent,
|
||||
NgFor,
|
||||
PluginCardComponent,
|
||||
EditButtonComponent,
|
||||
|
@ -39,12 +39,14 @@ export class PluginListInstalledComponent implements OnInit {
|
|||
totalItems: null
|
||||
}
|
||||
sort = 'name'
|
||||
hasMoreResults = true
|
||||
isLoading = true
|
||||
|
||||
plugins: PeerTubePlugin[] = []
|
||||
updating: { [name: string]: boolean } = {}
|
||||
uninstalling: { [name: string]: boolean } = {}
|
||||
|
||||
onDataSubject = new Subject<any[]>()
|
||||
private hasInitialized = new ReplaySubject<boolean>()
|
||||
|
||||
constructor (
|
||||
private pluginService: PluginService,
|
||||
|
@ -68,31 +70,35 @@ export class PluginListInstalledComponent implements OnInit {
|
|||
|
||||
this.pluginType = parseInt(query['pluginType'], 10) as PluginType_Type
|
||||
|
||||
this.reloadPlugins()
|
||||
this.hasInitialized.next(true)
|
||||
})
|
||||
}
|
||||
|
||||
reloadPlugins () {
|
||||
this.pagination.currentPage = 1
|
||||
this.plugins = []
|
||||
|
||||
this.loadMorePlugins()
|
||||
}
|
||||
|
||||
loadMorePlugins () {
|
||||
loadMorePlugins (reset = false) {
|
||||
this.isLoading = true
|
||||
this.pluginApiService.getPlugins(this.pluginType, this.pagination, this.sort)
|
||||
.subscribe({
|
||||
next: res => {
|
||||
if (reset) this.plugins = []
|
||||
this.plugins = this.plugins.concat(res.data)
|
||||
this.pagination.totalItems = res.total
|
||||
this.hasMoreResults = (this.pagination.itemsPerPage * this.pagination.currentPage) < this.pagination.totalItems
|
||||
|
||||
this.onDataSubject.next(res.data)
|
||||
this.isLoading = false
|
||||
},
|
||||
|
||||
error: err => this.notifier.error(err.message)
|
||||
})
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
this.hasInitialized.pipe(
|
||||
distinct(),
|
||||
filter(val => val)
|
||||
)
|
||||
.subscribe(() => this.loadMorePlugins(true))
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
if (!hasMoreItems(this.pagination)) return
|
||||
|
||||
|
|
|
@ -28,7 +28,14 @@
|
|||
No results.
|
||||
</div>
|
||||
|
||||
<div class="plugins" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
|
||||
<my-infinite-scroller
|
||||
class="plugins"
|
||||
[(currentPage)]="pagination.currentPage"
|
||||
[isLoading]="isSearching"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
(currentPageChange)="onPageChange()"
|
||||
[hasMore]="hasMoreResults"
|
||||
>
|
||||
<ng-container *ngFor="let plugin of plugins" >
|
||||
<my-plugin-card [plugin]="plugin" [version]="plugin.latestVersion" [pluginType]="pluginType">
|
||||
<div ngProjectAs="badges">
|
||||
|
@ -58,4 +65,4 @@
|
|||
|
||||
</my-plugin-card>
|
||||
</ng-container>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
|
|
|
@ -6,14 +6,14 @@ import { ComponentPagination, ConfirmService, hasMoreItems, Notifier, PluginServ
|
|||
import { AlertComponent } from '@app/shared/shared-main/common/alert.component'
|
||||
import { PeerTubePluginIndex, PluginType, PluginType_Type } from '@peertube/peertube-models'
|
||||
import { logger } from '@root-helpers/logger'
|
||||
import { Subject } from 'rxjs'
|
||||
import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
|
||||
import { ReplaySubject, Subject } from 'rxjs'
|
||||
import { debounceTime, distinct, distinctUntilChanged, filter } from 'rxjs/operators'
|
||||
import { GlobalIconComponent } from '../../../shared/shared-icons/global-icon.component'
|
||||
import { ButtonComponent } from '../../../shared/shared-main/buttons/button.component'
|
||||
import { EditButtonComponent } from '../../../shared/shared-main/buttons/edit-button.component'
|
||||
import { AutofocusDirective } from '../../../shared/shared-main/common/autofocus.directive'
|
||||
import { InfiniteScrollerDirective } from '../../../shared/shared-main/common/infinite-scroller.directive'
|
||||
import { PluginCardComponent } from '../shared/plugin-card.component'
|
||||
import { InfiniteScrollerComponent } from '../../../shared/shared-main/common/infinite-scroller.component'
|
||||
import { PluginNavigationComponent } from '../shared/plugin-navigation.component'
|
||||
|
||||
@Component({
|
||||
|
@ -26,7 +26,7 @@ import { PluginNavigationComponent } from '../shared/plugin-navigation.component
|
|||
NgIf,
|
||||
GlobalIconComponent,
|
||||
AutofocusDirective,
|
||||
InfiniteScrollerDirective,
|
||||
InfiniteScrollerComponent,
|
||||
NgFor,
|
||||
PluginCardComponent,
|
||||
EditButtonComponent,
|
||||
|
@ -43,17 +43,17 @@ export class PluginSearchComponent implements OnInit {
|
|||
totalItems: null
|
||||
}
|
||||
sort = '-trending'
|
||||
hasMoreResults = true
|
||||
|
||||
search = ''
|
||||
isSearching = false
|
||||
isSearching = true
|
||||
|
||||
plugins: PeerTubePluginIndex[] = []
|
||||
installing: { [name: string]: boolean } = {}
|
||||
pluginInstalled = false
|
||||
|
||||
onDataSubject = new Subject<any[]>()
|
||||
|
||||
private searchSubject = new Subject<string>()
|
||||
private hasInitialized = new ReplaySubject<boolean>()
|
||||
|
||||
constructor (
|
||||
private pluginService: PluginService,
|
||||
|
@ -75,10 +75,15 @@ export class PluginSearchComponent implements OnInit {
|
|||
this.route.queryParams.subscribe(query => {
|
||||
if (!query['pluginType']) return
|
||||
|
||||
const oldSearch = this.search
|
||||
this.pluginType = parseInt(query['pluginType'], 10) as PluginType_Type
|
||||
this.search = query['search'] || ''
|
||||
this.hasInitialized.next(true)
|
||||
|
||||
this.reloadPlugins()
|
||||
if (oldSearch !== this.search) {
|
||||
this.pagination.currentPage = 1
|
||||
this.onPageChange()
|
||||
}
|
||||
})
|
||||
|
||||
this.searchSubject.asObservable()
|
||||
|
@ -95,14 +100,7 @@ export class PluginSearchComponent implements OnInit {
|
|||
this.searchSubject.next(target.value)
|
||||
}
|
||||
|
||||
reloadPlugins () {
|
||||
this.pagination.currentPage = 1
|
||||
this.plugins = []
|
||||
|
||||
this.loadMorePlugins()
|
||||
}
|
||||
|
||||
loadMorePlugins () {
|
||||
loadMorePlugins (reset = false) {
|
||||
this.isSearching = true
|
||||
|
||||
this.pluginApiService.searchAvailablePlugins(this.pluginType, this.pagination, this.sort, this.search)
|
||||
|
@ -110,10 +108,11 @@ export class PluginSearchComponent implements OnInit {
|
|||
next: res => {
|
||||
this.isSearching = false
|
||||
|
||||
if (reset) this.plugins = []
|
||||
|
||||
this.plugins = this.plugins.concat(res.data)
|
||||
this.pagination.totalItems = res.total
|
||||
|
||||
this.onDataSubject.next(res.data)
|
||||
this.hasMoreResults = (this.pagination.itemsPerPage * this.pagination.currentPage) < this.pagination.totalItems
|
||||
},
|
||||
|
||||
error: err => {
|
||||
|
@ -125,6 +124,14 @@ export class PluginSearchComponent implements OnInit {
|
|||
})
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
this.hasInitialized.pipe(
|
||||
distinct(),
|
||||
filter(val => val)
|
||||
)
|
||||
.subscribe(() => this.loadMorePlugins(true))
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
if (!hasMoreItems(this.pagination)) return
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<my-channels-setup-message [hideLink]="true"></my-channels-setup-message>
|
||||
|
||||
<div class="video-channels-header d-flex justify-content-between gap-2">
|
||||
<my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
|
||||
<my-advanced-input-filter [emitOnInit]="false" (search)="onSearch($event)"></my-advanced-input-filter>
|
||||
|
||||
<a class="peertube-create-button" routerLink="/manage/create">
|
||||
<my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
|
||||
|
@ -26,7 +26,14 @@
|
|||
|
||||
<div class="no-results" i18n *ngIf="this.pagination.totalItems === 0">No channel found.</div>
|
||||
|
||||
<div class="video-channels" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onChannelDataSubject.asObservable()">
|
||||
<my-infinite-scroller
|
||||
class="video-channels"
|
||||
[(currentPage)]="pagination.currentPage"
|
||||
[isLoading]="isLoading"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
(currentPageChange)="onPageChange()"
|
||||
[hasMore]="hasMoreResults"
|
||||
>
|
||||
<div *ngFor="let videoChannel of videoChannels; let i = index" class="video-channel">
|
||||
<my-actor-avatar [actor]="videoChannel" actorType="channel" [internalHref]="[ '/c', videoChannel.nameWithHost ]" size="80"></my-actor-avatar>
|
||||
|
||||
|
@ -68,4 +75,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
|
|
|
@ -13,7 +13,7 @@ import { ActorAvatarComponent } from '../../shared/shared-actor-image/actor-avat
|
|||
import { AdvancedInputFilterComponent } from '../../shared/shared-forms/advanced-input-filter.component'
|
||||
import { GlobalIconComponent } from '../../shared/shared-icons/global-icon.component'
|
||||
import { DeferLoadingDirective } from '../../shared/shared-main/common/defer-loading.directive'
|
||||
import { InfiniteScrollerDirective } from '../../shared/shared-main/common/infinite-scroller.directive'
|
||||
import { InfiniteScrollerComponent } from '../../shared/shared-main/common/infinite-scroller.component'
|
||||
import { NumberFormatterPipe } from '../../shared/shared-main/common/number-formatter.pipe'
|
||||
import { DeleteButtonComponent } from '../../shared/shared-main/buttons/delete-button.component'
|
||||
import { EditButtonComponent } from '../../shared/shared-main/buttons/edit-button.component'
|
||||
|
@ -31,7 +31,7 @@ type CustomChartData = (ChartData & { startDate: string, total: number })
|
|||
RouterLink,
|
||||
ChannelsSetupMessageComponent,
|
||||
AdvancedInputFilterComponent,
|
||||
InfiniteScrollerDirective,
|
||||
InfiniteScrollerComponent,
|
||||
NgFor,
|
||||
ActorAvatarComponent,
|
||||
EditButtonComponent,
|
||||
|
@ -43,6 +43,8 @@ type CustomChartData = (ChartData & { startDate: string, total: number })
|
|||
})
|
||||
export class MyVideoChannelsComponent {
|
||||
videoChannels: VideoChannel[] = []
|
||||
hasMoreResults = true
|
||||
isLoading = true
|
||||
|
||||
videoChannelsChartData: CustomChartData[]
|
||||
|
||||
|
@ -76,10 +78,9 @@ export class MyVideoChannelsComponent {
|
|||
this.search = search
|
||||
|
||||
this.pagination.currentPage = 1
|
||||
this.videoChannels = []
|
||||
this.pagesDone.clear()
|
||||
|
||||
this.loadMoreVideoChannels()
|
||||
this.onPageChange()
|
||||
}
|
||||
|
||||
async deleteVideoChannel (videoChannel: VideoChannel) {
|
||||
|
@ -111,6 +112,10 @@ export class MyVideoChannelsComponent {
|
|||
})
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
this.loadMoreVideoChannels(true)
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
if (!hasMoreItems(this.pagination)) return
|
||||
|
||||
|
@ -119,9 +124,10 @@ export class MyVideoChannelsComponent {
|
|||
this.loadMoreVideoChannels()
|
||||
}
|
||||
|
||||
private loadMoreVideoChannels () {
|
||||
if (this.pagesDone.has(this.pagination.currentPage)) return
|
||||
private loadMoreVideoChannels (reset = false) {
|
||||
if (!reset && this.pagesDone.has(this.pagination.currentPage)) return
|
||||
this.pagesDone.add(this.pagination.currentPage)
|
||||
this.isLoading = true
|
||||
|
||||
return this.authService.userInformationLoaded
|
||||
.pipe(
|
||||
|
@ -136,6 +142,9 @@ export class MyVideoChannelsComponent {
|
|||
switchMap(options => this.videoChannelService.listAccountVideoChannels(options))
|
||||
)
|
||||
.subscribe(res => {
|
||||
this.isLoading = false
|
||||
this.hasMoreResults = res.data.length === this.pagination.itemsPerPage
|
||||
if (reset) this.videoChannels = []
|
||||
this.videoChannels = this.videoChannels.concat(res.data)
|
||||
this.pagination.totalItems = res.total
|
||||
|
||||
|
|
|
@ -7,12 +7,19 @@
|
|||
</div>
|
||||
|
||||
<div class="followers-header">
|
||||
<my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
|
||||
<my-advanced-input-filter [emitOnInit]="false" [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
|
||||
</div>
|
||||
|
||||
<div class="no-results" i18n *ngIf="pagination.totalItems === 0">No follower found.</div>
|
||||
|
||||
<div class="actors" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
|
||||
<my-infinite-scroller
|
||||
class="actors"
|
||||
[(currentPage)]="pagination.currentPage"
|
||||
[isLoading]="isLoading"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
(currentPageChange)="onPageChange()"
|
||||
[hasMore]="hasMoreResults"
|
||||
>
|
||||
<div *ngFor="let follow of follows" class="actor">
|
||||
<my-actor-avatar [actor]="follow.follower" actorType="account" [href]="follow.follower.url" size="40"></my-actor-avatar>
|
||||
|
||||
|
@ -28,4 +35,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
|
|
|
@ -4,21 +4,22 @@ import { ActivatedRoute } from '@angular/router'
|
|||
import { AuthService, ComponentPagination, Notifier } from '@app/core'
|
||||
import { UserSubscriptionService } from '@app/shared/shared-user-subscription/user-subscription.service'
|
||||
import { ActorFollow } from '@peertube/peertube-models'
|
||||
import { Subject } from 'rxjs'
|
||||
import { ActorAvatarComponent } from '../../shared/shared-actor-image/actor-avatar.component'
|
||||
import { AdvancedInputFilter, AdvancedInputFilterComponent } from '../../shared/shared-forms/advanced-input-filter.component'
|
||||
import { GlobalIconComponent } from '../../shared/shared-icons/global-icon.component'
|
||||
import { InfiniteScrollerDirective } from '../../shared/shared-main/common/infinite-scroller.directive'
|
||||
import { InfiniteScrollerComponent } from '../../shared/shared-main/common/infinite-scroller.component'
|
||||
import { formatICU } from '@app/helpers'
|
||||
|
||||
@Component({
|
||||
templateUrl: './my-followers.component.html',
|
||||
styleUrls: [ './my-followers.component.scss' ],
|
||||
standalone: true,
|
||||
imports: [ GlobalIconComponent, NgIf, AdvancedInputFilterComponent, InfiniteScrollerDirective, NgFor, ActorAvatarComponent ]
|
||||
imports: [ GlobalIconComponent, NgIf, AdvancedInputFilterComponent, InfiniteScrollerComponent, NgFor, ActorAvatarComponent ]
|
||||
})
|
||||
export class MyFollowersComponent implements OnInit {
|
||||
follows: ActorFollow[] = []
|
||||
hasMoreResults = true
|
||||
isLoading = true
|
||||
|
||||
pagination: ComponentPagination = {
|
||||
currentPage: 1,
|
||||
|
@ -26,7 +27,6 @@ export class MyFollowersComponent implements OnInit {
|
|||
totalItems: null
|
||||
}
|
||||
|
||||
onDataSubject = new Subject<any[]>()
|
||||
search: string
|
||||
|
||||
inputFilters: AdvancedInputFilter[]
|
||||
|
@ -58,6 +58,10 @@ export class MyFollowersComponent implements OnInit {
|
|||
]
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
this.loadFollowers(false)
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
// Last page
|
||||
if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
|
||||
|
@ -83,6 +87,8 @@ export class MyFollowersComponent implements OnInit {
|
|||
}
|
||||
|
||||
private loadFollowers (more = true) {
|
||||
this.isLoading = true
|
||||
|
||||
this.userSubscriptionService.listFollowers({
|
||||
pagination: this.pagination,
|
||||
nameWithHost: this.getUsername(),
|
||||
|
@ -93,8 +99,9 @@ export class MyFollowersComponent implements OnInit {
|
|||
? this.follows.concat(res.data)
|
||||
: res.data
|
||||
this.pagination.totalItems = res.total
|
||||
this.hasMoreResults = (this.pagination.itemsPerPage * this.pagination.currentPage) < this.pagination.totalItems
|
||||
|
||||
this.onDataSubject.next(res.data)
|
||||
this.isLoading = false
|
||||
},
|
||||
|
||||
error: err => this.notifier.error(err.message)
|
||||
|
|
|
@ -7,12 +7,19 @@
|
|||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
|
||||
<my-advanced-input-filter [emitOnInit]="false" (search)="onSearch($event)"></my-advanced-input-filter>
|
||||
</div>
|
||||
|
||||
<div class="no-results" i18n *ngIf="pagination.totalItems === 0">You don't have any subscription yet.</div>
|
||||
|
||||
<div class="actors" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
|
||||
<my-infinite-scroller
|
||||
class="actors"
|
||||
[(currentPage)]="pagination.currentPage"
|
||||
[isLoading]="isLoading"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
(currentPageChange)="onPageChange()"
|
||||
[hasMore]="hasMoreResults"
|
||||
>
|
||||
<div *ngFor="let videoChannel of videoChannels" class="actor">
|
||||
<my-actor-avatar [actor]="videoChannel" actorType="channel" [internalHref]="[ '/c', videoChannel.nameWithHost ]" size="80"></my-actor-avatar>
|
||||
|
||||
|
@ -33,4 +40,4 @@
|
|||
|
||||
<my-subscribe-button [videoChannels]="[videoChannel]"></my-subscribe-button>
|
||||
</div>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { Subject } from 'rxjs'
|
||||
import { Component } from '@angular/core'
|
||||
import { ComponentPagination, Notifier } from '@app/core'
|
||||
import { SubscribeButtonComponent } from '../../shared/shared-user-subscription/subscribe-button.component'
|
||||
import { RouterLink } from '@angular/router'
|
||||
import { ActorAvatarComponent } from '../../shared/shared-actor-image/actor-avatar.component'
|
||||
import { InfiniteScrollerDirective } from '../../shared/shared-main/common/infinite-scroller.directive'
|
||||
import { InfiniteScrollerComponent } from '../../shared/shared-main/common/infinite-scroller.component'
|
||||
import { AdvancedInputFilterComponent } from '../../shared/shared-forms/advanced-input-filter.component'
|
||||
import { NgIf, NgFor } from '@angular/common'
|
||||
import { GlobalIconComponent } from '../../shared/shared-icons/global-icon.component'
|
||||
|
@ -20,7 +19,7 @@ import { formatICU } from '@app/helpers'
|
|||
GlobalIconComponent,
|
||||
NgIf,
|
||||
AdvancedInputFilterComponent,
|
||||
InfiniteScrollerDirective,
|
||||
InfiniteScrollerComponent,
|
||||
NgFor,
|
||||
ActorAvatarComponent,
|
||||
RouterLink,
|
||||
|
@ -29,15 +28,15 @@ import { formatICU } from '@app/helpers'
|
|||
})
|
||||
export class MySubscriptionsComponent {
|
||||
videoChannels: VideoChannel[] = []
|
||||
hasMoreResults = true
|
||||
|
||||
isLoading = true
|
||||
pagination: ComponentPagination = {
|
||||
currentPage: 1,
|
||||
itemsPerPage: 10,
|
||||
totalItems: null
|
||||
}
|
||||
|
||||
onDataSubject = new Subject<any[]>()
|
||||
|
||||
search: string
|
||||
|
||||
constructor (
|
||||
|
@ -45,9 +44,15 @@ export class MySubscriptionsComponent {
|
|||
private notifier: Notifier
|
||||
) {}
|
||||
|
||||
onPageChange () {
|
||||
this.loadSubscriptions()
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
// Last page
|
||||
if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
|
||||
if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.pagination.currentPage += 1
|
||||
this.loadSubscriptions()
|
||||
|
@ -66,6 +71,7 @@ export class MySubscriptionsComponent {
|
|||
}
|
||||
|
||||
private loadSubscriptions (more = true) {
|
||||
this.isLoading = true
|
||||
this.userSubscriptionService.listSubscriptions({ pagination: this.pagination, search: this.search })
|
||||
.subscribe({
|
||||
next: res => {
|
||||
|
@ -73,8 +79,8 @@ export class MySubscriptionsComponent {
|
|||
? this.videoChannels.concat(res.data)
|
||||
: res.data
|
||||
this.pagination.totalItems = res.total
|
||||
|
||||
this.onDataSubject.next(res.data)
|
||||
this.hasMoreResults = (this.pagination.itemsPerPage * this.pagination.currentPage) < this.pagination.totalItems
|
||||
this.isLoading = false
|
||||
},
|
||||
|
||||
error: err => this.notifier.error(err.message)
|
||||
|
|
|
@ -33,9 +33,15 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="videos" myInfiniteScroller (nearOfBottom)="onNearOfBottom()"
|
||||
cdkDropList (cdkDropListDropped)="drop($event)" [dataObservable]="onDataSubject.asObservable()"
|
||||
<my-infinite-scroller
|
||||
class="videos"
|
||||
[(currentPage)]="pagination.currentPage"
|
||||
[isLoading]="isLoading"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
(currentPageChange)="onPageChange()"
|
||||
cdkDropList
|
||||
(cdkDropListDropped)="drop($event)"
|
||||
[hasMore]="hasMoreResults"
|
||||
>
|
||||
<div class="video" *ngFor="let playlistElement of playlistElements; trackBy: trackByFn" cdkDrag [cdkDragStartDelay]="getDragStartDelay()">
|
||||
<my-video-playlist-element-miniature
|
||||
|
@ -44,7 +50,7 @@
|
|||
>
|
||||
</my-video-playlist-element-miniature>
|
||||
</div>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -56,8 +56,8 @@
|
|||
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.video:last-child {
|
||||
border: 0;
|
||||
.video:not(:has(+ .video)) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.videos.cdk-drop-list-dragging .video:not(.cdk-drag-placeholder) {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { Subject, Subscription } from 'rxjs'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { CdkDragDrop, CdkDropList, CdkDrag } from '@angular/cdk/drag-drop'
|
||||
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { ComponentPagination, ConfirmService, HooksService, Notifier, ScreenService } from '@app/core'
|
||||
import { VideoPlaylistType } from '@peertube/peertube-models'
|
||||
import { VideoPlaylistElementMiniatureComponent } from '../../shared/shared-video-playlist/video-playlist-element-miniature.component'
|
||||
import { InfiniteScrollerDirective } from '../../shared/shared-main/common/infinite-scroller.directive'
|
||||
import { InfiniteScrollerComponent } from '../../shared/shared-main/common/infinite-scroller.component'
|
||||
import { ActionDropdownComponent, DropdownAction } from '../../shared/shared-main/buttons/action-dropdown.component'
|
||||
import { GlobalIconComponent } from '../../shared/shared-icons/global-icon.component'
|
||||
import { VideoPlaylistMiniatureComponent } from '../../shared/shared-video-playlist/video-playlist-miniature.component'
|
||||
|
@ -24,7 +24,7 @@ import { VideoPlaylistService } from '@app/shared/shared-video-playlist/video-pl
|
|||
VideoPlaylistMiniatureComponent,
|
||||
GlobalIconComponent,
|
||||
ActionDropdownComponent,
|
||||
InfiniteScrollerDirective,
|
||||
InfiniteScrollerComponent,
|
||||
CdkDropList,
|
||||
NgFor,
|
||||
CdkDrag,
|
||||
|
@ -35,6 +35,8 @@ import { VideoPlaylistService } from '@app/shared/shared-video-playlist/video-pl
|
|||
export class MyVideoPlaylistElementsComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('videoShareModal') videoShareModal: VideoShareComponent
|
||||
|
||||
hasMoreResults = true
|
||||
isLoading = true
|
||||
playlistElements: VideoPlaylistElement[] = []
|
||||
playlist: VideoPlaylist
|
||||
|
||||
|
@ -46,8 +48,6 @@ export class MyVideoPlaylistElementsComponent implements OnInit, OnDestroy {
|
|||
totalItems: null
|
||||
}
|
||||
|
||||
onDataSubject = new Subject<any[]>()
|
||||
|
||||
private videoPlaylistId: string | number
|
||||
private paramsSub: Subscription
|
||||
|
||||
|
@ -122,6 +122,10 @@ export class MyVideoPlaylistElementsComponent implements OnInit, OnDestroy {
|
|||
this.reorderClientPositions(oldFirst)
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
this.loadElements(true)
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
// Last page
|
||||
if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
|
||||
|
@ -175,7 +179,9 @@ export class MyVideoPlaylistElementsComponent implements OnInit, OnDestroy {
|
|||
return null
|
||||
}
|
||||
|
||||
private loadElements () {
|
||||
private loadElements (reset = false) {
|
||||
this.isLoading = true
|
||||
|
||||
this.hooks.wrapObsFun(
|
||||
this.videoPlaylistService.getPlaylistVideos.bind(this.videoPlaylistService),
|
||||
{ videoPlaylistId: this.videoPlaylistId, componentPagination: this.pagination },
|
||||
|
@ -184,10 +190,13 @@ export class MyVideoPlaylistElementsComponent implements OnInit, OnDestroy {
|
|||
'filter:api.my-library.video-playlist-elements.list.result'
|
||||
)
|
||||
.subscribe(({ total, data }) => {
|
||||
if (reset) this.playlistElements = []
|
||||
|
||||
this.playlistElements = this.playlistElements.concat(data)
|
||||
this.pagination.totalItems = total
|
||||
this.hasMoreResults = (this.pagination.itemsPerPage * this.pagination.currentPage) < this.pagination.totalItems
|
||||
|
||||
this.onDataSubject.next(data)
|
||||
this.isLoading = false
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<my-channels-setup-message></my-channels-setup-message>
|
||||
|
||||
<div class="video-playlists-header d-flex justify-content-between gap-2">
|
||||
<my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
|
||||
<my-advanced-input-filter [emitOnInit]="false" (search)="onSearch($event)"></my-advanced-input-filter>
|
||||
|
||||
<a class="peertube-create-button" routerLink="create">
|
||||
<my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
|
||||
|
@ -17,7 +17,14 @@
|
|||
</a>
|
||||
</div>
|
||||
|
||||
<div class="video-playlists" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
|
||||
<my-infinite-scroller
|
||||
class="video-playlists"
|
||||
[(currentPage)]="pagination.currentPage"
|
||||
[isLoading]="isLoading"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
(currentPageChange)="onPageChange()"
|
||||
[hasMore]="hasMoreResults"
|
||||
>
|
||||
<div *ngFor="let playlist of videoPlaylists" class="video-playlist">
|
||||
<my-video-playlist-miniature
|
||||
[playlist]="playlist" [toManage]="true" [displayChannel]="true"
|
||||
|
@ -30,4 +37,4 @@
|
|||
<my-edit-button label [ptRouterLink]="[ 'update', playlist.shortUUID ]"></my-edit-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { Subject } from 'rxjs'
|
||||
import { mergeMap } from 'rxjs/operators'
|
||||
import { Component } from '@angular/core'
|
||||
import { AuthService, ComponentPagination, ConfirmService, Notifier } from '@app/core'
|
||||
|
@ -6,7 +5,7 @@ import { VideoPlaylistType } from '@peertube/peertube-models'
|
|||
import { EditButtonComponent } from '../../shared/shared-main/buttons/edit-button.component'
|
||||
import { DeleteButtonComponent } from '../../shared/shared-main/buttons/delete-button.component'
|
||||
import { VideoPlaylistMiniatureComponent } from '../../shared/shared-video-playlist/video-playlist-miniature.component'
|
||||
import { InfiniteScrollerDirective } from '../../shared/shared-main/common/infinite-scroller.directive'
|
||||
import { InfiniteScrollerComponent } from '../../shared/shared-main/common/infinite-scroller.component'
|
||||
import { RouterLink } from '@angular/router'
|
||||
import { AdvancedInputFilterComponent } from '../../shared/shared-forms/advanced-input-filter.component'
|
||||
import { ChannelsSetupMessageComponent } from '../../shared/shared-main/channel/channels-setup-message.component'
|
||||
|
@ -26,7 +25,7 @@ import { formatICU } from '@app/helpers'
|
|||
ChannelsSetupMessageComponent,
|
||||
AdvancedInputFilterComponent,
|
||||
RouterLink,
|
||||
InfiniteScrollerDirective,
|
||||
InfiniteScrollerComponent,
|
||||
NgFor,
|
||||
VideoPlaylistMiniatureComponent,
|
||||
DeleteButtonComponent,
|
||||
|
@ -35,6 +34,8 @@ import { formatICU } from '@app/helpers'
|
|||
})
|
||||
export class MyVideoPlaylistsComponent {
|
||||
videoPlaylists: VideoPlaylist[] = []
|
||||
hasMoreResults = true
|
||||
isLoading = true
|
||||
|
||||
pagination: ComponentPagination = {
|
||||
currentPage: 1,
|
||||
|
@ -42,8 +43,6 @@ export class MyVideoPlaylistsComponent {
|
|||
totalItems: null
|
||||
}
|
||||
|
||||
onDataSubject = new Subject<any[]>()
|
||||
|
||||
search: string
|
||||
|
||||
constructor (
|
||||
|
@ -77,6 +76,10 @@ export class MyVideoPlaylistsComponent {
|
|||
return playlist.type.id === VideoPlaylistType.REGULAR
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
this.loadVideoPlaylists(true)
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
// Last page
|
||||
if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
|
||||
|
@ -98,6 +101,8 @@ export class MyVideoPlaylistsComponent {
|
|||
}
|
||||
|
||||
private loadVideoPlaylists (reset = false) {
|
||||
this.isLoading = true
|
||||
|
||||
this.authService.userInformationLoaded
|
||||
.pipe(mergeMap(() => {
|
||||
const user = this.authService.getUser()
|
||||
|
@ -108,8 +113,9 @@ export class MyVideoPlaylistsComponent {
|
|||
|
||||
this.videoPlaylists = this.videoPlaylists.concat(res.data)
|
||||
this.pagination.totalItems = res.total
|
||||
this.hasMoreResults = (this.pagination.itemsPerPage * this.pagination.currentPage) < this.pagination.totalItems
|
||||
|
||||
this.onDataSubject.next(res.data)
|
||||
this.isLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
<div myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onSearchDataSubject.asObservable()" class="search-result">
|
||||
<my-infinite-scroller
|
||||
[(currentPage)]="pagination.currentPage"
|
||||
[isLoading]="isSearching"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
(currentPageChange)="onPageChange()"
|
||||
class="search-result"
|
||||
[hasMore]="hasMoreResults"
|
||||
>
|
||||
<div class="results-header">
|
||||
<div class="first-line">
|
||||
<div class="results-counter" *ngIf="pagination.totalItems">
|
||||
|
@ -79,4 +86,4 @@
|
|||
</div>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
|
|
|
@ -11,11 +11,11 @@ import { AdvancedSearch } from '@app/shared/shared-search/advanced-search.model'
|
|||
import { SearchService } from '@app/shared/shared-search/search.service'
|
||||
import { VideoPlaylist } from '@app/shared/shared-video-playlist/video-playlist.model'
|
||||
import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { HTMLServerConfig, SearchTargetType } from '@peertube/peertube-models'
|
||||
import { forkJoin, Subject, Subscription } from 'rxjs'
|
||||
import { HTMLServerConfig, ResultList, SearchTargetType } from '@peertube/peertube-models'
|
||||
import { forkJoin, Subscription } from 'rxjs'
|
||||
import { LinkType } from 'src/types/link.type'
|
||||
import { ActorAvatarComponent } from '../shared/shared-actor-image/actor-avatar.component'
|
||||
import { InfiniteScrollerDirective } from '../shared/shared-main/common/infinite-scroller.directive'
|
||||
import { InfiniteScrollerComponent } from '../shared/shared-main/common/infinite-scroller.component'
|
||||
import { NumberFormatterPipe } from '../shared/shared-main/common/number-formatter.pipe'
|
||||
import { SubscribeButtonComponent } from '../shared/shared-user-subscription/subscribe-button.component'
|
||||
import { MiniatureDisplayOptions, VideoMiniatureComponent } from '../shared/shared-video-miniature/video-miniature.component'
|
||||
|
@ -28,7 +28,7 @@ import { SearchFiltersComponent } from './search-filters.component'
|
|||
templateUrl: './search.component.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
InfiniteScrollerDirective,
|
||||
InfiniteScrollerComponent,
|
||||
NgIf,
|
||||
NgbCollapse,
|
||||
SearchFiltersComponent,
|
||||
|
@ -52,6 +52,8 @@ export class SearchComponent implements OnInit, OnDestroy {
|
|||
currentPage: 1,
|
||||
totalItems: null as number
|
||||
}
|
||||
hasMoreResults = true
|
||||
isSearching = false
|
||||
advancedSearch: AdvancedSearch = new AdvancedSearch()
|
||||
isSearchFilterCollapsed = true
|
||||
currentSearch: string
|
||||
|
@ -71,14 +73,9 @@ export class SearchComponent implements OnInit, OnDestroy {
|
|||
|
||||
userMiniature: User
|
||||
|
||||
onSearchDataSubject = new Subject<any>()
|
||||
|
||||
private subActivatedRoute: Subscription
|
||||
private isInitialLoad = false // set to false to show the search filters on first arrival
|
||||
|
||||
private hasMoreResults = true
|
||||
private isSearching = false
|
||||
|
||||
private lastSearchTarget: SearchTargetType
|
||||
|
||||
private serverConfig: HTMLServerConfig
|
||||
|
@ -123,8 +120,6 @@ export class SearchComponent implements OnInit, OnDestroy {
|
|||
// Don't hide filters if we have some of them AND the user just came on the webpage, or we have an error
|
||||
this.isSearchFilterCollapsed = !this.error && (this.isInitialLoad === false || !this.advancedSearch.containsValues())
|
||||
this.isInitialLoad = false
|
||||
|
||||
this.search()
|
||||
},
|
||||
|
||||
error: err => this.notifier.error(err.message)
|
||||
|
@ -175,9 +170,7 @@ export class SearchComponent implements OnInit, OnDestroy {
|
|||
this.pagination.totalItems = results.reduce((p, r) => p += r.total, 0)
|
||||
this.lastSearchTarget = this.advancedSearch.searchTarget
|
||||
|
||||
this.hasMoreResults = this.results.length < this.pagination.totalItems
|
||||
|
||||
this.onSearchDataSubject.next(results)
|
||||
this.hasMoreResults = this.calculateHasMoreResults(results)
|
||||
},
|
||||
|
||||
error: err => {
|
||||
|
@ -200,6 +193,11 @@ export class SearchComponent implements OnInit, OnDestroy {
|
|||
})
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
this.results = []
|
||||
this.search()
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
// Last page
|
||||
if (!this.hasMoreResults || this.isSearching) return
|
||||
|
@ -280,7 +278,7 @@ export class SearchComponent implements OnInit, OnDestroy {
|
|||
this.pagination.currentPage = 1
|
||||
this.pagination.totalItems = null
|
||||
|
||||
this.results = []
|
||||
this.onPageChange()
|
||||
}
|
||||
|
||||
private updateTitle () {
|
||||
|
@ -303,7 +301,7 @@ export class SearchComponent implements OnInit, OnDestroy {
|
|||
private getVideosObs () {
|
||||
const params = {
|
||||
search: this.currentSearch,
|
||||
componentPagination: immutableAssign(this.pagination, { itemsPerPage: 10 }),
|
||||
componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.buildVideosPerPage() }),
|
||||
advancedSearch: this.advancedSearch
|
||||
}
|
||||
|
||||
|
@ -366,6 +364,28 @@ export class SearchComponent implements OnInit, OnDestroy {
|
|||
return undefined
|
||||
}
|
||||
|
||||
private calculateHasMoreResults (results: [ResultList<VideoChannel>, ResultList<VideoPlaylist>, ResultList<Video>]) {
|
||||
const [ channels, playlists, videos ] = results
|
||||
|
||||
if ((this.pagination.currentPage * this.buildChannelsPerPage()) < channels.total) {
|
||||
return true
|
||||
}
|
||||
|
||||
if ((this.pagination.currentPage * this.buildPlaylistsPerPage()) < playlists.total) {
|
||||
return true
|
||||
}
|
||||
|
||||
if ((this.pagination.currentPage * this.buildVideosPerPage()) < videos.total) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private buildVideosPerPage () {
|
||||
return 10
|
||||
}
|
||||
|
||||
private buildChannelsPerPage () {
|
||||
if (this.advancedSearch.resultType === 'channels') return 10
|
||||
|
||||
|
|
|
@ -5,9 +5,16 @@
|
|||
|
||||
<div i18n class="no-results" *ngIf="pagination.totalItems === 0">This channel does not have playlists.</div>
|
||||
|
||||
<div class="playlists" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
|
||||
<my-infinite-scroller
|
||||
class="playlists"
|
||||
[(currentPage)]="pagination.currentPage"
|
||||
[isLoading]="isLoading"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
(currentPageChange)="onPageChange()"
|
||||
[hasMore]="hasMoreResults"
|
||||
>
|
||||
<div *ngFor="let playlist of videoPlaylists" class="playlist-wrapper">
|
||||
<my-video-playlist-miniature [playlist]="playlist" [toManage]="false" [displayAsRow]="displayAsRow()"></my-video-playlist-miniature>
|
||||
</div>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Subject, Subscription } from 'rxjs'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'
|
||||
import { ComponentPagination, hasMoreItems, HooksService, ScreenService } from '@app/core'
|
||||
import { VideoPlaylistMiniatureComponent } from '../../shared/shared-video-playlist/video-playlist-miniature.component'
|
||||
import { InfiniteScrollerDirective } from '../../shared/shared-main/common/infinite-scroller.directive'
|
||||
import { InfiniteScrollerComponent } from '../../shared/shared-main/common/infinite-scroller.component'
|
||||
import { NgIf, NgFor } from '@angular/common'
|
||||
import { VideoChannel } from '@app/shared/shared-main/channel/video-channel.model'
|
||||
import { VideoChannelService } from '@app/shared/shared-main/channel/video-channel.service'
|
||||
|
@ -14,7 +14,7 @@ import { VideoPlaylistService } from '@app/shared/shared-video-playlist/video-pl
|
|||
templateUrl: './video-channel-playlists.component.html',
|
||||
styleUrls: [ './video-channel-playlists.component.scss' ],
|
||||
standalone: true,
|
||||
imports: [ NgIf, InfiniteScrollerDirective, NgFor, VideoPlaylistMiniatureComponent ]
|
||||
imports: [ NgIf, InfiniteScrollerComponent, NgFor, VideoPlaylistMiniatureComponent ]
|
||||
})
|
||||
export class VideoChannelPlaylistsComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
videoPlaylists: VideoPlaylist[] = []
|
||||
|
@ -24,8 +24,8 @@ export class VideoChannelPlaylistsComponent implements OnInit, AfterViewInit, On
|
|||
itemsPerPage: 20,
|
||||
totalItems: null
|
||||
}
|
||||
|
||||
onDataSubject = new Subject<any[]>()
|
||||
hasMoreResults = true
|
||||
isLoading = false
|
||||
|
||||
private videoChannelSub: Subscription
|
||||
private videoChannel: VideoChannel
|
||||
|
@ -46,8 +46,6 @@ export class VideoChannelPlaylistsComponent implements OnInit, AfterViewInit, On
|
|||
this.hooks.runAction('action:video-channel-playlists.video-channel.loaded', 'video-channel', { videoChannel })
|
||||
|
||||
this.videoPlaylists = []
|
||||
this.pagination.currentPage = 1
|
||||
this.loadVideoPlaylists()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -59,6 +57,10 @@ export class VideoChannelPlaylistsComponent implements OnInit, AfterViewInit, On
|
|||
if (this.videoChannelSub) this.videoChannelSub.unsubscribe()
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
this.loadVideoPlaylists(true)
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
if (!hasMoreItems(this.pagination)) return
|
||||
|
||||
|
@ -70,15 +72,19 @@ export class VideoChannelPlaylistsComponent implements OnInit, AfterViewInit, On
|
|||
return this.screenService.isInMobileView()
|
||||
}
|
||||
|
||||
private loadVideoPlaylists () {
|
||||
private loadVideoPlaylists (reset = false) {
|
||||
this.isLoading = true
|
||||
|
||||
this.videoPlaylistService.listChannelPlaylists(this.videoChannel, this.pagination)
|
||||
.subscribe(res => {
|
||||
if (reset) this.videoPlaylists = []
|
||||
this.videoPlaylists = this.videoPlaylists.concat(res.data)
|
||||
this.pagination.totalItems = res.total
|
||||
this.hasMoreResults = this.videoPlaylists.length < this.pagination.totalItems
|
||||
|
||||
this.hooks.runAction('action:video-channel-playlists.playlists.loaded', 'video-channel', { playlists: this.videoPlaylists })
|
||||
|
||||
this.onDataSubject.next(res.data)
|
||||
this.isLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,14 @@
|
|||
|
||||
<div *ngIf="totalNotDeletedComments === 0 && comments.length === 0" i18n>No comments.</div>
|
||||
|
||||
<div class="comment-threads" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
|
||||
<my-infinite-scroller
|
||||
class="comment-threads"
|
||||
[(currentPage)]="componentPagination.currentPage"
|
||||
[isLoading]="isLoading"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
(currentPageChange)="onPageChange($event)"
|
||||
[hasMore]="hasMoreResults"
|
||||
>
|
||||
<div>
|
||||
<div class="anchor" #commentHighlightBlock id="highlighted-comment"></div>
|
||||
<my-video-comment
|
||||
|
@ -90,7 +97,7 @@
|
|||
</my-video-comment>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
} @else {
|
||||
<div i18n>Comments are disabled.</div>
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ import { VideoComment } from '@app/shared/shared-video-comment/video-comment.mod
|
|||
import { VideoCommentService } from '@app/shared/shared-video-comment/video-comment.service'
|
||||
import { NgbDropdown, NgbDropdownButtonItem, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { PeerTubeProblemDocument, ServerErrorCode, VideoCommentPolicy } from '@peertube/peertube-models'
|
||||
import { Subject, Subscription } from 'rxjs'
|
||||
import { InfiniteScrollerDirective } from '../../../../shared/shared-main/common/infinite-scroller.directive'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { InfiniteScrollerComponent } from '../../../../shared/shared-main/common/infinite-scroller.component'
|
||||
import { FeedComponent } from '../../../../shared/shared-main/feeds/feed.component'
|
||||
import { LoaderComponent } from '../../../../shared/shared-main/common/loader.component'
|
||||
import { VideoCommentAddComponent } from './video-comment-add.component'
|
||||
|
@ -31,7 +31,7 @@ import { VideoCommentComponent } from './video-comment.component'
|
|||
NgbDropdownItem,
|
||||
NgIf,
|
||||
VideoCommentAddComponent,
|
||||
InfiniteScrollerDirective,
|
||||
InfiniteScrollerComponent,
|
||||
VideoCommentComponent,
|
||||
NgFor,
|
||||
LoaderComponent
|
||||
|
@ -56,6 +56,8 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
|
|||
totalItems: null
|
||||
}
|
||||
totalNotDeletedComments: number
|
||||
hasMoreResults = true
|
||||
isLoading = false
|
||||
|
||||
inReplyToCommentId: number
|
||||
commentReplyRedraftValue: string
|
||||
|
@ -68,8 +70,6 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
|
|||
|
||||
syndicationItems: Syndication[] = []
|
||||
|
||||
onDataSubject = new Subject<any[]>()
|
||||
|
||||
private sub: Subscription
|
||||
|
||||
constructor (
|
||||
|
@ -161,13 +161,16 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
|
|||
'filter:api.video-watch.video-threads.list.result'
|
||||
)
|
||||
|
||||
this.isLoading = true
|
||||
|
||||
obs.subscribe({
|
||||
next: res => {
|
||||
this.comments = this.comments.concat(res.data)
|
||||
this.componentPagination.totalItems = res.total
|
||||
this.totalNotDeletedComments = res.totalNotDeletedComments
|
||||
this.hasMoreResults = hasMoreItems(this.componentPagination)
|
||||
|
||||
this.onDataSubject.next(res.data)
|
||||
this.isLoading = false
|
||||
|
||||
this.hooks.runAction('action:video-watch.video-threads.loaded', 'video-watch', { data: this.componentPagination })
|
||||
},
|
||||
|
@ -277,6 +280,10 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
|
|||
return this.authService.isLoggedIn()
|
||||
}
|
||||
|
||||
onPageChange (newPage: number) {
|
||||
this.resetVideo(newPage)
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
if (hasMoreItems(this.componentPagination)) {
|
||||
this.componentPagination.currentPage++
|
||||
|
@ -291,7 +298,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
|
|||
comment.account = null
|
||||
}
|
||||
|
||||
private resetVideo () {
|
||||
private resetVideo (page = 1) {
|
||||
if (this.video.commentsPolicy.id === VideoCommentPolicy.DISABLED) return
|
||||
|
||||
// Reset all our fields
|
||||
|
@ -300,7 +307,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
|
|||
this.threadComments = {}
|
||||
this.threadLoading = {}
|
||||
this.inReplyToCommentId = undefined
|
||||
this.componentPagination.currentPage = 1
|
||||
this.componentPagination.currentPage = page
|
||||
this.componentPagination.totalItems = null
|
||||
this.totalNotDeletedComments = null
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
background-color: pvar(--mainBackgroundColor);
|
||||
overflow-y: auto;
|
||||
border-bottom: 1px solid $separator-border-color;
|
||||
display: block;
|
||||
|
||||
.widget-header {
|
||||
background-color: pvar(--submenuBackgroundColor);
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
<div
|
||||
*ngIf="playlist && (currentPlaylistPosition || noPlaylistVideos)" class="widget-root playlist"
|
||||
myInfiniteScroller [onItself]="true" (nearOfBottom)="onPlaylistVideosNearOfBottom()"
|
||||
<my-infinite-scroller
|
||||
*ngIf="playlist && (currentPlaylistPosition || noPlaylistVideos)"
|
||||
class="widget-root playlist"
|
||||
[(currentPage)]="playlistPagination.currentPage"
|
||||
[isLoading]="isLoading"
|
||||
[hasMore]="hasMoreResults"
|
||||
[onItself]="true"
|
||||
(nearOfBottom)="onPlaylistVideosNearOfBottom()"
|
||||
(currentPageChange)="onPageChange()"
|
||||
>
|
||||
<div class="widget-header playlist-info">
|
||||
<div class="widget-title playlist-display-name">
|
||||
|
@ -44,4 +50,4 @@
|
|||
[touchScreenEditButton]="true"
|
||||
></my-video-playlist-element-miniature>
|
||||
</div>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
::ng-deep .load-more {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
my-video-playlist-element-miniature {
|
||||
::ng-deep {
|
||||
.video {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
||||
import { Router } from '@angular/router'
|
||||
import { AuthService, ComponentPagination, HooksService, Notifier, SessionStorageService, UserService } from '@app/core'
|
||||
import { AuthService, ComponentPagination, hasMoreItems, HooksService, Notifier, SessionStorageService, UserService } from '@app/core'
|
||||
import { isInViewport } from '@app/helpers'
|
||||
import { getBoolOrDefault } from '@root-helpers/local-storage-utils'
|
||||
import { peertubeSessionStorage } from '@root-helpers/peertube-web-storage'
|
||||
|
@ -8,7 +8,7 @@ import { VideoPlaylistPrivacy } from '@peertube/peertube-models'
|
|||
import { VideoPlaylistElementMiniatureComponent } from '../../../../shared/shared-video-playlist/video-playlist-element-miniature.component'
|
||||
import { GlobalIconComponent } from '../../../../shared/shared-icons/global-icon.component'
|
||||
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { InfiniteScrollerDirective } from '../../../../shared/shared-main/common/infinite-scroller.directive'
|
||||
import { InfiniteScrollerComponent } from '../../../../shared/shared-main/common/infinite-scroller.component'
|
||||
import { NgIf, NgClass, NgFor } from '@angular/common'
|
||||
import { VideoPlaylist } from '@app/shared/shared-video-playlist/video-playlist.model'
|
||||
import { VideoPlaylistElement } from '@app/shared/shared-video-playlist/video-playlist-element.model'
|
||||
|
@ -19,7 +19,7 @@ import { VideoPlaylistService } from '@app/shared/shared-video-playlist/video-pl
|
|||
templateUrl: './video-watch-playlist.component.html',
|
||||
styleUrls: [ './player-widget.component.scss', './video-watch-playlist.component.scss' ],
|
||||
standalone: true,
|
||||
imports: [ NgIf, InfiniteScrollerDirective, NgClass, NgbTooltip, GlobalIconComponent, NgFor, VideoPlaylistElementMiniatureComponent ]
|
||||
imports: [ NgIf, InfiniteScrollerComponent, NgClass, NgbTooltip, GlobalIconComponent, NgFor, VideoPlaylistElementMiniatureComponent ]
|
||||
})
|
||||
export class VideoWatchPlaylistComponent {
|
||||
static SESSION_STORAGE_LOOP_PLAYLIST = 'loop_playlist'
|
||||
|
@ -29,6 +29,9 @@ export class VideoWatchPlaylistComponent {
|
|||
@Output() videoFound = new EventEmitter<string>()
|
||||
@Output() noVideoFound = new EventEmitter<void>()
|
||||
|
||||
hasMoreResults = true
|
||||
isLoading = true
|
||||
|
||||
playlistElements: VideoPlaylistElement[] = []
|
||||
playlistPagination: ComponentPagination = {
|
||||
currentPage: 1,
|
||||
|
@ -42,7 +45,7 @@ export class VideoWatchPlaylistComponent {
|
|||
loopPlaylist: boolean
|
||||
loopPlaylistSwitchText = ''
|
||||
|
||||
noPlaylistVideos = false
|
||||
noPlaylistVideos = true
|
||||
currentPlaylistPosition: number
|
||||
|
||||
constructor (
|
||||
|
@ -63,6 +66,14 @@ export class VideoWatchPlaylistComponent {
|
|||
this.setLoopPlaylistSwitchText()
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
// Prevent triggering upon initial page load
|
||||
if (this.isLoading) return
|
||||
|
||||
this.playlistElements = []
|
||||
this.loadPlaylistElements(this.playlist, false)
|
||||
}
|
||||
|
||||
onPlaylistVideosNearOfBottom (position?: number) {
|
||||
// Last page
|
||||
if (this.playlistPagination.totalItems <= (this.playlistPagination.currentPage * this.playlistPagination.itemsPerPage)) return
|
||||
|
@ -103,10 +114,13 @@ export class VideoWatchPlaylistComponent {
|
|||
'filter:api.video-watch.video-playlist-elements.get.params',
|
||||
'filter:api.video-watch.video-playlist-elements.get.result'
|
||||
)
|
||||
this.isLoading = true
|
||||
|
||||
obs.subscribe(({ total, data: playlistElements }) => {
|
||||
this.playlistElements = this.playlistElements.concat(playlistElements)
|
||||
this.playlistPagination.totalItems = total
|
||||
this.hasMoreResults = hasMoreItems(this.playlistPagination)
|
||||
this.isLoading = false
|
||||
|
||||
const firstAvailableVideo = this.playlistElements.find(e => !!e.video)
|
||||
if (!firstAvailableVideo) {
|
||||
|
|
|
@ -3,8 +3,12 @@
|
|||
|
||||
<div class="no-results" i18n *ngIf="notResults">No results.</div>
|
||||
|
||||
<div
|
||||
myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()"
|
||||
<my-infinite-scroller
|
||||
[(currentPage)]="currentPage"
|
||||
[isLoading]="isLoading"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
(currentPageChange)="onPageChange()"
|
||||
[hasMore]="hasMoreResults"
|
||||
>
|
||||
<ng-container *ngFor="let overview of overviews">
|
||||
|
||||
|
@ -47,6 +51,6 @@
|
|||
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Subject, Subscription, switchMap } from 'rxjs'
|
||||
import { Subscription, switchMap } from 'rxjs'
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||
import { Notifier, ScreenService, User, UserService } from '@app/core'
|
||||
import { Video } from '@app/shared/shared-main/video/video.model'
|
||||
|
@ -7,7 +7,7 @@ import { VideosOverview } from './videos-overview.model'
|
|||
import { ActorAvatarComponent } from '../../../shared/shared-actor-image/actor-avatar.component'
|
||||
import { VideoMiniatureComponent } from '../../../shared/shared-video-miniature/video-miniature.component'
|
||||
import { RouterLink } from '@angular/router'
|
||||
import { InfiniteScrollerDirective } from '../../../shared/shared-main/common/infinite-scroller.directive'
|
||||
import { InfiniteScrollerComponent } from '../../../shared/shared-main/common/infinite-scroller.component'
|
||||
import { NgIf, NgFor } from '@angular/common'
|
||||
|
||||
@Component({
|
||||
|
@ -15,21 +15,21 @@ import { NgIf, NgFor } from '@angular/common'
|
|||
templateUrl: './video-overview.component.html',
|
||||
styleUrls: [ './video-overview.component.scss' ],
|
||||
standalone: true,
|
||||
imports: [ NgIf, InfiniteScrollerDirective, NgFor, RouterLink, VideoMiniatureComponent, ActorAvatarComponent ]
|
||||
imports: [ NgIf, InfiniteScrollerComponent, NgFor, RouterLink, VideoMiniatureComponent, ActorAvatarComponent ]
|
||||
})
|
||||
export class VideoOverviewComponent implements OnInit, OnDestroy {
|
||||
onDataSubject = new Subject<any>()
|
||||
hasMoreResults = true
|
||||
|
||||
overviews: VideosOverview[] = []
|
||||
notResults = false
|
||||
|
||||
userMiniature: User
|
||||
currentPage = 1
|
||||
isLoading = true
|
||||
|
||||
private loaded = false
|
||||
private currentPage = 1
|
||||
private maxPage = 20
|
||||
private lastWasEmpty = false
|
||||
private isLoading = false
|
||||
|
||||
private userSub: Subscription
|
||||
|
||||
|
@ -74,22 +74,26 @@ export class VideoOverviewComponent implements OnInit, OnDestroy {
|
|||
return videos.slice(0, numberOfVideos * 2)
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
this.loadMoreResults(true)
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
if (this.currentPage >= this.maxPage) return
|
||||
if (this.lastWasEmpty) return
|
||||
if (this.isLoading) return
|
||||
|
||||
this.currentPage++
|
||||
this.loadMoreResults()
|
||||
}
|
||||
|
||||
private loadMoreResults () {
|
||||
private loadMoreResults (reset = false) {
|
||||
this.isLoading = true
|
||||
|
||||
this.overviewService.getVideosOverview(this.currentPage)
|
||||
.subscribe({
|
||||
next: overview => {
|
||||
this.isLoading = false
|
||||
this.hasMoreResults = this.currentPage < this.maxPage
|
||||
|
||||
if (overview.tags.length === 0 && overview.channels.length === 0 && overview.categories.length === 0) {
|
||||
this.lastWasEmpty = true
|
||||
|
@ -99,8 +103,8 @@ export class VideoOverviewComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
this.loaded = true
|
||||
this.onDataSubject.next(overview)
|
||||
|
||||
if (reset) this.overviews = []
|
||||
this.overviews.push(overview)
|
||||
},
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<ng-content></ng-content>
|
||||
|
||||
<div *ngIf="hasMore && !isLoading" class="load-more d-flex justify-content-center">
|
||||
<a class="peertube-button-link orange-button" i18n [routerLink]="['.']" [queryParams]="{ page: currentPage + 1}" [queryParamsHandling]="'merge'">
|
||||
Load more
|
||||
</a>
|
||||
</div>
|
|
@ -0,0 +1,4 @@
|
|||
.load-more {
|
||||
grid-column-start: 1;
|
||||
grid-column-end: -1;
|
||||
}
|
|
@ -1,16 +1,26 @@
|
|||
import { fromEvent, Observable, Subscription } from 'rxjs'
|
||||
import { fromEvent, Subscription } from 'rxjs'
|
||||
import { distinctUntilChanged, filter, map, share, startWith, throttleTime } from 'rxjs/operators'
|
||||
import { AfterViewChecked, Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'
|
||||
import { AfterViewChecked, Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'
|
||||
import { PeerTubeRouterService, RouterSetting } from '@app/core'
|
||||
import { I18nSelectPipe, NgIf } from '@angular/common'
|
||||
import { ActivatedRoute, NavigationEnd, Router, RouterLink } from '@angular/router'
|
||||
|
||||
@Directive({
|
||||
selector: '[myInfiniteScroller]',
|
||||
standalone: true
|
||||
@Component({
|
||||
selector: 'my-infinite-scroller',
|
||||
standalone: true,
|
||||
templateUrl: './infinite-scroller.component.html',
|
||||
styleUrl: './infinite-scroller.component.scss',
|
||||
imports: [
|
||||
NgIf,
|
||||
RouterLink,
|
||||
I18nSelectPipe
|
||||
]
|
||||
})
|
||||
export class InfiniteScrollerDirective implements OnInit, OnDestroy, AfterViewChecked {
|
||||
export class InfiniteScrollerComponent implements OnInit, OnDestroy, AfterViewChecked {
|
||||
@Input() hasMore: boolean
|
||||
@Input() isLoading: boolean
|
||||
@Input() percentLimit = 70
|
||||
@Input() onItself = false
|
||||
@Input() dataObservable: Observable<any[]>
|
||||
|
||||
// Add angular state in query params to reuse the routed component
|
||||
@Input() setAngularState: boolean
|
||||
|
@ -18,37 +28,61 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy, AfterViewCh
|
|||
|
||||
@Output() nearOfBottom = new EventEmitter<void>()
|
||||
|
||||
@Input() currentPage!: number
|
||||
@Output() currentPageChange = new EventEmitter<number>()
|
||||
|
||||
private disabled: boolean
|
||||
|
||||
private decimalLimit = 0
|
||||
private lastCurrentBottom = -1
|
||||
private scrollDownSub: Subscription
|
||||
private container: HTMLElement
|
||||
|
||||
private checkScroll = false
|
||||
private routeEventSub: Subscription
|
||||
|
||||
constructor (
|
||||
private peertubeRouter: PeerTubeRouterService,
|
||||
private el: ElementRef
|
||||
private el: ElementRef,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router
|
||||
) {
|
||||
this.decimalLimit = this.percentLimit / 100
|
||||
}
|
||||
|
||||
ngAfterViewChecked () {
|
||||
if (this.checkScroll) {
|
||||
this.checkScroll = false
|
||||
|
||||
if (this.hasMore && !this.isLoading) {
|
||||
// Wait HTML update
|
||||
setTimeout(() => {
|
||||
if (this.hasScroll() === false) this.nearOfBottom.emit()
|
||||
if (this.hasScroll() === false && !this.disabled) this.nearOfBottom.emit()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.disabled = !!this.route.snapshot.queryParams.page
|
||||
|
||||
this.changePage(+this.route.snapshot.queryParams['page'] || 1)
|
||||
|
||||
this.routeEventSub = this.router.events
|
||||
.pipe(
|
||||
filter(event => event instanceof NavigationEnd)
|
||||
)
|
||||
.subscribe((event: NavigationEnd) => {
|
||||
const search = event.url.split('?')[1]
|
||||
const params = new URLSearchParams(search)
|
||||
const newPage = +params.get('page') || 1
|
||||
|
||||
if (newPage === this.currentPage) return
|
||||
|
||||
this.changePage(newPage)
|
||||
})
|
||||
|
||||
this.initialize()
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
if (this.scrollDownSub) this.scrollDownSub.unsubscribe()
|
||||
if (this.routeEventSub) this.routeEventSub.unsubscribe()
|
||||
}
|
||||
|
||||
initialize () {
|
||||
|
@ -78,14 +112,13 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy, AfterViewCh
|
|||
.subscribe(() => {
|
||||
if (this.setAngularState && !this.parentDisabled) this.setScrollRouteParams()
|
||||
|
||||
this.nearOfBottom.emit()
|
||||
if (!this.disabled) this.nearOfBottom.emit()
|
||||
})
|
||||
}
|
||||
|
||||
if (this.dataObservable) {
|
||||
this.dataObservable
|
||||
.pipe(filter(d => d.length !== 0))
|
||||
.subscribe(() => this.checkScroll = true)
|
||||
}
|
||||
private changePage (newPage: number) {
|
||||
this.currentPage = newPage
|
||||
this.currentPageChange.emit(newPage)
|
||||
}
|
||||
|
||||
private getScrollInfo () {
|
|
@ -40,10 +40,15 @@
|
|||
|
||||
<div class="no-results" i18n *ngIf="hasDoneFirstQuery && videos.length === 0 && highlightedLives.length === 0">No results.</div>
|
||||
|
||||
<div
|
||||
myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onVideosDataSubject.asObservable()"
|
||||
[setAngularState]="true" [parentDisabled]="disabled"
|
||||
class="videos" [ngClass]="{ 'display-as-row': displayAsRow }"
|
||||
<my-infinite-scroller
|
||||
[(currentPage)]="pagination.currentPage"
|
||||
(currentPageChange)="onPageChange()"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
[hasMore]="hasMoreResults"
|
||||
[setAngularState]="true"
|
||||
[parentDisabled]="disabled"
|
||||
class="videos"
|
||||
[ngClass]="{ 'display-as-row': displayAsRow }"
|
||||
>
|
||||
<ng-container *ngIf="highlightedLives.length !== 0">
|
||||
<h2 class="date-title">
|
||||
|
@ -80,11 +85,5 @@
|
|||
</my-video-miniature>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-center">
|
||||
<a *ngIf="lastQueryLength === this.pagination.itemsPerPage" class="peertube-button-link orange-button" i18n [routerLink]="['.']" [queryParams]="{ page: pagination.currentPage + 1}" [queryParamsHandling]="'merge'">
|
||||
Load more
|
||||
</a>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common'
|
||||
import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, booleanAttribute } from '@angular/core'
|
||||
import { ActivatedRoute, NavigationEnd, Router, RouterLink, RouterLinkActive } from '@angular/router'
|
||||
import { ActivatedRoute, RouterLink, RouterLinkActive } from '@angular/router'
|
||||
import {
|
||||
AuthService,
|
||||
ComponentPaginationLight,
|
||||
|
@ -18,8 +18,8 @@ import { ResultList, UserRight, VideoSortField } from '@peertube/peertube-models
|
|||
import { logger } from '@root-helpers/logger'
|
||||
import debug from 'debug'
|
||||
import { Observable, Subject, Subscription, forkJoin, fromEvent, of } from 'rxjs'
|
||||
import { concatMap, debounceTime, filter, map, switchMap } from 'rxjs/operators'
|
||||
import { InfiniteScrollerDirective } from '../shared-main/common/infinite-scroller.directive'
|
||||
import { concatMap, debounceTime, map, switchMap } from 'rxjs/operators'
|
||||
import { InfiniteScrollerComponent } from '../shared-main/common/infinite-scroller.component'
|
||||
import { ButtonComponent } from '../shared-main/buttons/button.component'
|
||||
import { FeedComponent } from '../shared-main/feeds/feed.component'
|
||||
import { Syndication } from '../shared-main/feeds/syndication.model'
|
||||
|
@ -65,10 +65,9 @@ enum GroupDate {
|
|||
NgTemplateOutlet,
|
||||
ButtonComponent,
|
||||
VideoFiltersHeaderComponent,
|
||||
InfiniteScrollerDirective,
|
||||
InfiniteScrollerComponent,
|
||||
VideoMiniatureComponent,
|
||||
GlobalIconComponent,
|
||||
RouterLink
|
||||
GlobalIconComponent
|
||||
]
|
||||
})
|
||||
export class VideosListComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
@ -98,11 +97,12 @@ export class VideosListComponent implements OnInit, OnChanges, OnDestroy {
|
|||
|
||||
@Input() displayOptions: MiniatureDisplayOptions
|
||||
|
||||
@Input({ transform: booleanAttribute }) disabled: boolean
|
||||
@Input({ transform: booleanAttribute }) disabled = false
|
||||
|
||||
@Output() filtersChanged = new EventEmitter<VideoFilters>()
|
||||
@Output() videosLoaded = new EventEmitter<Video[]>()
|
||||
|
||||
hasMoreResults = true
|
||||
videos: Video[] = []
|
||||
highlightedLives: Video[] = []
|
||||
|
||||
|
@ -119,8 +119,6 @@ export class VideosListComponent implements OnInit, OnChanges, OnDestroy {
|
|||
itemsPerPage: 25
|
||||
}
|
||||
|
||||
lastQueryLength: number
|
||||
|
||||
private defaultDisplayOptions: MiniatureDisplayOptions = {
|
||||
date: true,
|
||||
views: true,
|
||||
|
@ -138,6 +136,8 @@ export class VideosListComponent implements OnInit, OnChanges, OnDestroy {
|
|||
private groupedDateLabels: { [id in GroupDate]: string }
|
||||
private groupedDates: { [id: number]: GroupDate } = {}
|
||||
|
||||
private lastQueryLength: number
|
||||
|
||||
private videoRequests = new Subject<{
|
||||
reset: boolean
|
||||
obsVideos: Observable<Pick<ResultList<Video>, 'data'>>
|
||||
|
@ -153,32 +153,13 @@ export class VideosListComponent implements OnInit, OnChanges, OnDestroy {
|
|||
private route: ActivatedRoute,
|
||||
private screenService: ScreenService,
|
||||
private peertubeRouter: PeerTubeRouterService,
|
||||
private serverService: ServerService,
|
||||
public router: Router
|
||||
private serverService: ServerService
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.subscribeToVideoRequests()
|
||||
this.disabled = this.disabled || this.route.snapshot.queryParams.finiteScroll === 'true'
|
||||
|
||||
this.router.events
|
||||
.pipe(
|
||||
filter(event => event instanceof NavigationEnd)
|
||||
)
|
||||
.subscribe((event: NavigationEnd) => {
|
||||
const search = event.url.split('?')[1]
|
||||
const params = new URLSearchParams(search)
|
||||
const newPage = +params.get('page') || this.pagination.currentPage
|
||||
|
||||
if (newPage === this.pagination.currentPage) {
|
||||
return
|
||||
}
|
||||
|
||||
this.pagination.currentPage = newPage
|
||||
this.loadMoreVideos(true)
|
||||
})
|
||||
|
||||
const hiddenFilters = this.hideScopeFilter
|
||||
? [ 'scope' ]
|
||||
|
@ -211,8 +192,6 @@ export class VideosListComponent implements OnInit, OnChanges, OnDestroy {
|
|||
this.loadUserSettings(user)
|
||||
}
|
||||
|
||||
this.scheduleOnFiltersChanged(false)
|
||||
|
||||
this.subscribeToAnonymousUpdate()
|
||||
this.subscribeToSearchChange()
|
||||
})
|
||||
|
@ -272,6 +251,10 @@ export class VideosListComponent implements OnInit, OnChanges, OnDestroy {
|
|||
return video.id
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
this.loadMoreVideos(true)
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
if (this.disabled) return
|
||||
|
||||
|
@ -312,7 +295,6 @@ export class VideosListComponent implements OnInit, OnChanges, OnDestroy {
|
|||
}
|
||||
|
||||
reloadVideos () {
|
||||
this.pagination.currentPage = +this.route.snapshot.queryParams.page || 1
|
||||
this.loadMoreVideos(true)
|
||||
}
|
||||
|
||||
|
@ -504,6 +486,7 @@ export class VideosListComponent implements OnInit, OnChanges, OnDestroy {
|
|||
.subscribe({
|
||||
next: ({ videos, highlightedLives, reset }) => {
|
||||
this.hasDoneFirstQuery = true
|
||||
this.hasMoreResults = videos.length === this.pagination.itemsPerPage
|
||||
this.lastQueryLength = videos.length
|
||||
|
||||
if (reset) {
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
<div class="no-results" i18n *ngIf="hasDoneFirstQuery && videos.length === 0">{{ noResultMessage }}</div>
|
||||
|
||||
<div
|
||||
<my-infinite-scroller
|
||||
class="videos"
|
||||
myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()"
|
||||
[parentDisabled]="disabled" [setAngularState]="true"
|
||||
[(currentPage)]="pagination.currentPage"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
[isLoading]="isLoading"
|
||||
(currentPageChange)="onPageChange()"
|
||||
[hasMore]="hasMoreResults"
|
||||
[parentDisabled]="disabled"
|
||||
[setAngularState]="true"
|
||||
>
|
||||
<div class="video" *ngFor="let video of videos; let i = index; trackBy: videoById">
|
||||
|
||||
|
@ -32,4 +37,4 @@
|
|||
<ng-container *ngTemplateOutlet="rowButtonsTemplate; context: {$implicit: video}"></ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Observable, Subject } from 'rxjs'
|
||||
import { Observable } from 'rxjs'
|
||||
import { AfterContentInit, Component, ContentChildren, EventEmitter, Input, Output, QueryList, TemplateRef } from '@angular/core'
|
||||
import { ComponentPagination, Notifier, User } from '@app/core'
|
||||
import { logger } from '@root-helpers/logger'
|
||||
|
@ -7,7 +7,7 @@ import { ResultList, VideosExistInPlaylists, VideoSortField } from '@peertube/pe
|
|||
import { MiniatureDisplayOptions, VideoMiniatureComponent } from './video-miniature.component'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { PeertubeCheckboxComponent } from '../shared-forms/peertube-checkbox.component'
|
||||
import { InfiniteScrollerDirective } from '../shared-main/common/infinite-scroller.directive'
|
||||
import { InfiniteScrollerComponent } from '../shared-main/common/infinite-scroller.component'
|
||||
import { NgIf, NgFor, NgTemplateOutlet } from '@angular/common'
|
||||
import { Video } from '../shared-main/video/video.model'
|
||||
import { PeerTubeTemplateDirective } from '../shared-main/common/peertube-template.directive'
|
||||
|
@ -19,7 +19,7 @@ export type SelectionType = { [ id: number ]: boolean }
|
|||
templateUrl: './videos-selection.component.html',
|
||||
styleUrls: [ './videos-selection.component.scss' ],
|
||||
standalone: true,
|
||||
imports: [ NgIf, InfiniteScrollerDirective, NgFor, PeertubeCheckboxComponent, FormsModule, VideoMiniatureComponent, NgTemplateOutlet ]
|
||||
imports: [ NgIf, InfiniteScrollerComponent, NgFor, PeertubeCheckboxComponent, FormsModule, VideoMiniatureComponent, NgTemplateOutlet ]
|
||||
})
|
||||
export class VideosSelectionComponent implements AfterContentInit {
|
||||
@Input() videosContainedInPlaylists: VideosExistInPlaylists
|
||||
|
@ -44,14 +44,14 @@ export class VideosSelectionComponent implements AfterContentInit {
|
|||
|
||||
_selection: SelectionType = {}
|
||||
|
||||
hasMoreResults = true
|
||||
isLoading = true
|
||||
rowButtonsTemplate: TemplateRef<any>
|
||||
globalButtonsTemplate: TemplateRef<any>
|
||||
|
||||
videos: Video[] = []
|
||||
sort: VideoSortField = '-publishedAt'
|
||||
|
||||
onDataSubject = new Subject<any[]>()
|
||||
|
||||
hasDoneFirstQuery = false
|
||||
|
||||
private lastQueryLength: number
|
||||
|
@ -88,8 +88,6 @@ export class VideosSelectionComponent implements AfterContentInit {
|
|||
const t = this.templates.find(t => t.name === 'globalButtons')
|
||||
if (t) this.globalButtonsTemplate = t.template
|
||||
}
|
||||
|
||||
this.loadMoreVideos()
|
||||
}
|
||||
|
||||
getVideosObservable (page: number) {
|
||||
|
@ -108,6 +106,10 @@ export class VideosSelectionComponent implements AfterContentInit {
|
|||
return video.id
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
this.loadMoreVideos(true)
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
if (this.disabled) return
|
||||
|
||||
|
@ -121,10 +123,12 @@ export class VideosSelectionComponent implements AfterContentInit {
|
|||
|
||||
loadMoreVideos (reset = false) {
|
||||
if (reset) this.hasDoneFirstQuery = false
|
||||
this.isLoading = true
|
||||
|
||||
this.getVideosObservable(this.pagination.currentPage)
|
||||
.subscribe({
|
||||
next: ({ data }) => {
|
||||
this.hasMoreResults = data.length === this.pagination.itemsPerPage
|
||||
this.hasDoneFirstQuery = true
|
||||
this.lastQueryLength = data.length
|
||||
|
||||
|
@ -132,7 +136,7 @@ export class VideosSelectionComponent implements AfterContentInit {
|
|||
this.videos = this.videos.concat(data)
|
||||
this.videosModel = this.videos
|
||||
|
||||
this.onDataSubject.next(data)
|
||||
this.isLoading = false
|
||||
},
|
||||
|
||||
error: err => {
|
||||
|
@ -146,7 +150,7 @@ export class VideosSelectionComponent implements AfterContentInit {
|
|||
|
||||
reloadVideos () {
|
||||
this.pagination.currentPage = 1
|
||||
this.loadMoreVideos(true)
|
||||
this.onPageChange()
|
||||
}
|
||||
|
||||
removeVideoFromArray (video: Video) {
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
<div *ngIf="componentPagination.totalItems === 0" class="no-notification" i18n>You don't have notifications.</div>
|
||||
|
||||
<div class="notifications" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
|
||||
<my-infinite-scroller
|
||||
class="notifications"
|
||||
[(currentPage)]="componentPagination.currentPage"
|
||||
[isLoading]="isLoading"
|
||||
(currentPageChange)="onPageChange()"
|
||||
(nearOfBottom)="onNearOfBottom()"
|
||||
[hasMore]="hasMoreResults"
|
||||
>
|
||||
<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->
|
||||
<div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }" (click)="markAsRead(notification)">
|
||||
|
||||
|
@ -258,4 +265,4 @@
|
|||
|
||||
<div [title]="notification.createdAt" class="from-date">{{ notification.createdAt | myFromNow }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</my-infinite-scroller>
|
||||
|
|
|
@ -6,7 +6,7 @@ import { CommonModule } from '@angular/common'
|
|||
import { GlobalIconComponent } from '../shared-icons/global-icon.component'
|
||||
import { RouterLink } from '@angular/router'
|
||||
import { FromNowPipe } from '../shared-main/date/from-now.pipe'
|
||||
import { InfiniteScrollerDirective } from '../shared-main/common/infinite-scroller.directive'
|
||||
import { InfiniteScrollerComponent } from '../shared-main/common/infinite-scroller.component'
|
||||
import { UserNotificationService } from '../shared-main/users/user-notification.service'
|
||||
import { UserNotification } from '../shared-main/users/user-notification.model'
|
||||
|
||||
|
@ -15,7 +15,7 @@ import { UserNotification } from '../shared-main/users/user-notification.model'
|
|||
templateUrl: 'user-notifications.component.html',
|
||||
styleUrls: [ 'user-notifications.component.scss' ],
|
||||
standalone: true,
|
||||
imports: [ CommonModule, GlobalIconComponent, RouterLink, FromNowPipe, InfiniteScrollerDirective ]
|
||||
imports: [ CommonModule, GlobalIconComponent, RouterLink, FromNowPipe, InfiniteScrollerComponent ]
|
||||
})
|
||||
export class UserNotificationsComponent implements OnInit {
|
||||
@Input() ignoreLoadingBar = false
|
||||
|
@ -29,8 +29,8 @@ export class UserNotificationsComponent implements OnInit {
|
|||
sortField = 'createdAt'
|
||||
|
||||
componentPagination: ComponentPagination
|
||||
|
||||
onDataSubject = new Subject<any[]>()
|
||||
hasMoreResults = true
|
||||
isLoading = true
|
||||
|
||||
constructor (
|
||||
private userNotificationService: UserNotificationService,
|
||||
|
@ -44,8 +44,6 @@ export class UserNotificationsComponent implements OnInit {
|
|||
totalItems: null
|
||||
}
|
||||
|
||||
this.loadNotifications()
|
||||
|
||||
if (this.markAllAsReadSubject) {
|
||||
this.markAllAsReadSubject.subscribe(() => this.markAllAsRead())
|
||||
}
|
||||
|
@ -61,22 +59,27 @@ export class UserNotificationsComponent implements OnInit {
|
|||
order: this.sortField === 'createdAt' ? -1 : 1
|
||||
}
|
||||
}
|
||||
this.isLoading = true
|
||||
|
||||
this.userNotificationService.listMyNotifications(options)
|
||||
.subscribe({
|
||||
next: result => {
|
||||
this.notifications = reset ? result.data : this.notifications.concat(result.data)
|
||||
this.componentPagination.totalItems = result.total
|
||||
this.hasMoreResults = hasMoreItems(this.componentPagination)
|
||||
|
||||
this.notificationsLoaded.emit()
|
||||
|
||||
this.onDataSubject.next(result.data)
|
||||
this.isLoading = false
|
||||
},
|
||||
|
||||
error: err => this.notifier.error(err.message)
|
||||
})
|
||||
}
|
||||
|
||||
onPageChange () {
|
||||
this.loadNotifications(true)
|
||||
}
|
||||
|
||||
onNearOfBottom () {
|
||||
if (this.infiniteScroll === false) return
|
||||
|
||||
|
|
Loading…
Reference in New Issue