+
diff --git a/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts
index 8c4d62d7d7b..0dfc9e37155 100644
--- a/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts
+++ b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts
@@ -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()
+ 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
})
}
}
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.html b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.html
index b2cbd01f63e..ab7a5e301ce 100644
--- a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.html
+++ b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.html
@@ -28,7 +28,14 @@
No comments.
-
+
} @else {
Comments are disabled.
}
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts
index 2eeda38bf0f..6c4cd8aa85c 100644
--- a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts
@@ -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
()
-
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
diff --git a/client/src/app/+videos/+video-watch/shared/player-widgets/player-widget.component.scss b/client/src/app/+videos/+video-watch/shared/player-widgets/player-widget.component.scss
index 13163d4ee67..cbe5d6a4a79 100644
--- a/client/src/app/+videos/+video-watch/shared/player-widgets/player-widget.component.scss
+++ b/client/src/app/+videos/+video-watch/shared/player-widgets/player-widget.component.scss
@@ -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);
diff --git a/client/src/app/+videos/+video-watch/shared/player-widgets/video-watch-playlist.component.html b/client/src/app/+videos/+video-watch/shared/player-widgets/video-watch-playlist.component.html
index 3cbfa33ad88..84141ee8699 100644
--- a/client/src/app/+videos/+video-watch/shared/player-widgets/video-watch-playlist.component.html
+++ b/client/src/app/+videos/+video-watch/shared/player-widgets/video-watch-playlist.component.html
@@ -1,6 +1,12 @@
-
+
diff --git a/client/src/app/+videos/+video-watch/shared/player-widgets/video-watch-playlist.component.scss b/client/src/app/+videos/+video-watch/shared/player-widgets/video-watch-playlist.component.scss
index 24c9fb5c068..dd2faf166c2 100644
--- a/client/src/app/+videos/+video-watch/shared/player-widgets/video-watch-playlist.component.scss
+++ b/client/src/app/+videos/+video-watch/shared/player-widgets/video-watch-playlist.component.scss
@@ -27,6 +27,10 @@
}
}
+ ::ng-deep .load-more {
+ margin: 20px 0;
+ }
+
my-video-playlist-element-miniature {
::ng-deep {
.video {
diff --git a/client/src/app/+videos/+video-watch/shared/player-widgets/video-watch-playlist.component.ts b/client/src/app/+videos/+video-watch/shared/player-widgets/video-watch-playlist.component.ts
index f0fc359ca60..4525a533cfb 100644
--- a/client/src/app/+videos/+video-watch/shared/player-widgets/video-watch-playlist.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/player-widgets/video-watch-playlist.component.ts
@@ -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
()
@Output() noVideoFound = new EventEmitter()
+ 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) {
diff --git a/client/src/app/+videos/video-list/overview/video-overview.component.html b/client/src/app/+videos/video-list/overview/video-overview.component.html
index b38516af82e..35fc6da2b50 100644
--- a/client/src/app/+videos/video-list/overview/video-overview.component.html
+++ b/client/src/app/+videos/video-list/overview/video-overview.component.html
@@ -3,8 +3,12 @@ Discover
No results.
-
@@ -47,6 +51,6 @@ {{ object.channel.displayName }}
-
+
diff --git a/client/src/app/+videos/video-list/overview/video-overview.component.ts b/client/src/app/+videos/video-list/overview/video-overview.component.ts
index f5bf7dba05e..0f6c4d7b255 100644
--- a/client/src/app/+videos/video-list/overview/video-overview.component.ts
+++ b/client/src/app/+videos/video-list/overview/video-overview.component.ts
@@ -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()
+ 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)
},
diff --git a/client/src/app/shared/shared-main/common/infinite-scroller.component.html b/client/src/app/shared/shared-main/common/infinite-scroller.component.html
new file mode 100644
index 00000000000..8a00e78a105
--- /dev/null
+++ b/client/src/app/shared/shared-main/common/infinite-scroller.component.html
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/client/src/app/shared/shared-main/common/infinite-scroller.component.scss b/client/src/app/shared/shared-main/common/infinite-scroller.component.scss
new file mode 100644
index 00000000000..61deddbe92f
--- /dev/null
+++ b/client/src/app/shared/shared-main/common/infinite-scroller.component.scss
@@ -0,0 +1,4 @@
+.load-more {
+ grid-column-start: 1;
+ grid-column-end: -1;
+}
diff --git a/client/src/app/shared/shared-main/common/infinite-scroller.directive.ts b/client/src/app/shared/shared-main/common/infinite-scroller.component.ts
similarity index 59%
rename from client/src/app/shared/shared-main/common/infinite-scroller.directive.ts
rename to client/src/app/shared/shared-main/common/infinite-scroller.component.ts
index 2ad446e9287..75dbfd1e640 100644
--- a/client/src/app/shared/shared-main/common/infinite-scroller.directive.ts
+++ b/client/src/app/shared/shared-main/common/infinite-scroller.component.ts
@@ -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'
-
-@Directive({
- selector: '[myInfiniteScroller]',
- standalone: true
+import { I18nSelectPipe, NgIf } from '@angular/common'
+import { ActivatedRoute, NavigationEnd, Router, RouterLink } from '@angular/router'
+
+@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
// 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()
+ @Input() currentPage!: number
+ @Output() currentPageChange = new EventEmitter()
+
+ 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 () {
diff --git a/client/src/app/shared/shared-video-miniature/videos-list.component.html b/client/src/app/shared/shared-video-miniature/videos-list.component.html
index 5b9bc786286..951d887df33 100644
--- a/client/src/app/shared/shared-video-miniature/videos-list.component.html
+++ b/client/src/app/shared/shared-video-miniature/videos-list.component.html
@@ -40,10 +40,15 @@ No results.
-