Add infinite scroll for comments

This commit is contained in:
Arkadiusz Fal 2022-01-05 17:12:32 +01:00
parent ac755d0ee6
commit 1db4a3197d
5 changed files with 84 additions and 47 deletions

View File

@ -8,7 +8,6 @@ final class CommentsModel: ObservableObject {
@Published var nextPage: String?
@Published var firstPage = true
@Published var loading = false
@Published var loaded = false
@Published var disabled = false
@ -41,37 +40,43 @@ final class CommentsModel: ObservableObject {
}
func load(page: String? = nil) {
guard Self.enabled, !loading else {
guard Self.enabled else {
return
}
reset()
loading = true
guard !Self.instance.isNil,
!(player?.currentVideo.isNil ?? true)
else {
return
}
if !firstPage && !nextPageAvailable {
return
}
firstPage = page.isNil || page!.isEmpty
api?.comments(player.currentVideo!.videoID, page: page)?
.load()
.onSuccess { [weak self] response in
if let page: CommentsPage = response.typedContent() {
self?.all = page.comments
self?.all += page.comments
self?.nextPage = page.nextPage
self?.disabled = page.disabled
}
}
.onCompletion { [weak self] _ in
self?.loading = false
self?.loaded = true
}
}
func loadNextPageIfNeeded(current comment: Comment) {
let thresholdIndex = all.index(all.endIndex, offsetBy: -5)
if all.firstIndex(where: { $0 == comment }) == thresholdIndex {
loadNextPage()
}
}
func loadNextPage() {
load(page: nextPage)
}
@ -108,7 +113,6 @@ final class CommentsModel: ObservableObject {
firstPage = true
nextPage = nil
loaded = false
loading = false
replies = []
repliesLoaded = false
}

View File

@ -1,6 +1,7 @@
import SwiftUI
struct CommentsView: View {
var embedInScrollView = false
@State private var repliesID: Comment.ID?
@EnvironmentObject<CommentsModel> private var comments
@ -8,53 +9,37 @@ struct CommentsView: View {
var body: some View {
Group {
if comments.disabled {
Text("Comments are disabled for this video")
.foregroundColor(.secondary)
NoCommentsView(text: "Comments are disabled", systemImage: "xmark.circle.fill")
} else if comments.loaded && comments.all.isEmpty {
Text("No comments")
.foregroundColor(.secondary)
NoCommentsView(text: "No comments", systemImage: "0.circle.fill")
} else if !comments.loaded {
PlaceholderProgressView()
.onAppear {
comments.load()
}
} else {
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .leading) {
let last = comments.all.last
let commentsStack = LazyVStack {
ForEach(comments.all) { comment in
CommentView(comment: comment, repliesID: $repliesID)
.onAppear {
comments.loadNextPageIfNeeded(current: comment)
}
.padding(.bottom, comment == last ? 5 : 0)
if comment != last {
Divider()
.padding(.vertical, 5)
}
}
HStack {
if comments.nextPageAvailable {
Button {
repliesID = nil
comments.loadNextPage()
} label: {
Label("Show more", systemImage: "arrow.turn.down.right")
}
}
if !comments.firstPage {
Button {
repliesID = nil
comments.load(page: nil)
} label: {
Label("Show first", systemImage: "arrow.turn.down.left")
}
}
}
.font(.system(size: 13))
.buttonStyle(.plain)
.padding(.vertical, 8)
.foregroundColor(.secondary)
if embedInScrollView {
ScrollView(.vertical, showsIndicators: false) {
commentsStack
}
} else {
commentsStack
}
}
}

View File

@ -0,0 +1,28 @@
import SwiftUI
struct NoCommentsView: View {
var text: String
var systemImage: String
var body: some View {
VStack(alignment: .center, spacing: 10) {
Image(systemName: systemImage)
.font(.system(size: 36))
Text(text)
#if !os(tvOS)
.font(.system(size: 12))
#endif
}
.frame(minWidth: 0, maxWidth: .infinity)
#if !os(tvOS)
.foregroundColor(.secondary)
#endif
}
}
struct NoCommentsView_Previews: PreviewProvider {
static var previews: some View {
NoCommentsView(text: "No comments", systemImage: "xmark.circle.fill")
}
}

View File

@ -512,6 +512,9 @@
37DD87C7271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; };
37DD87C8271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; };
37DD87C9271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; };
37DD9DA32785BBC900539416 /* NoCommentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DA22785BBC900539416 /* NoCommentsView.swift */; };
37DD9DA42785BBC900539416 /* NoCommentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DA22785BBC900539416 /* NoCommentsView.swift */; };
37DD9DA52785BBC900539416 /* NoCommentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DA22785BBC900539416 /* NoCommentsView.swift */; };
37E04C0F275940FB00172673 /* VerticalScrollingFix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E04C0E275940FB00172673 /* VerticalScrollingFix.swift */; };
37E084AC2753D95F00039B7D /* AccountsNavigationLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */; };
37E084AD2753D95F00039B7D /* AccountsNavigationLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */; };
@ -774,6 +777,7 @@
37D526E22720B4BE00ED2F5E /* View+SwipeGesture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+SwipeGesture.swift"; sourceTree = "<group>"; };
37D9169A27388A81002B1BAA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerStreams.swift; sourceTree = "<group>"; };
37DD9DA22785BBC900539416 /* NoCommentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoCommentsView.swift; sourceTree = "<group>"; };
37E04C0E275940FB00172673 /* VerticalScrollingFix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerticalScrollingFix.swift; sourceTree = "<group>"; };
37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsNavigationLink.swift; sourceTree = "<group>"; };
37E2EEAA270656EC00170416 /* PlayerControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerControlsView.swift; sourceTree = "<group>"; };
@ -909,6 +913,7 @@
children = (
371B7E602759706A00D21217 /* CommentsView.swift */,
37EF9A75275BEB8E0043B585 /* CommentView.swift */,
37DD9DA22785BBC900539416 /* NoCommentsView.swift */,
37B81B0126D2CAE700675966 /* PlaybackBar.swift */,
37BE0BD226A1D4780092E2DB /* Player.swift */,
3743CA4D270EFE3400E4D32B /* PlayerQueueRow.swift */,
@ -1914,6 +1919,7 @@
37FB28412721B22200A57617 /* ContentItem.swift in Sources */,
37C3A24D272360470087A57A /* ChannelPlaylist+Fixtures.swift in Sources */,
37484C2D26FC844700287258 /* InstanceSettings.swift in Sources */,
37DD9DA32785BBC900539416 /* NoCommentsView.swift in Sources */,
377A20A92693C9A2002842B8 /* TypedContentAccessors.swift in Sources */,
37484C3126FCB8F900287258 /* AccountValidator.swift in Sources */,
37319F0527103F94004ECCD0 /* PlayerQueue.swift in Sources */,
@ -1992,6 +1998,7 @@
374C053627242D9F009BDDBE /* ServicesSettings.swift in Sources */,
37BA794826DC2E56002A0235 /* AppSidebarSubscriptions.swift in Sources */,
37E70928271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */,
37DD9DA42785BBC900539416 /* NoCommentsView.swift in Sources */,
37EAD86C267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
37C3A24E272360470087A57A /* ChannelPlaylist+Fixtures.swift in Sources */,
37CEE4C22677B697005A1EFE /* Stream.swift in Sources */,
@ -2316,6 +2323,7 @@
37484C2726FC83E000287258 /* InstanceForm.swift in Sources */,
37F49BA826CB0FCE00304AC0 /* PlaylistFormView.swift in Sources */,
373197DA2732060100EF734F /* RelatedView.swift in Sources */,
37DD9DA52785BBC900539416 /* NoCommentsView.swift in Sources */,
37D4B19926717E1500C925CA /* Video.swift in Sources */,
378E50FD26FE8B9F00F49626 /* Instance.swift in Sources */,
37169AA82729E2CC0011DE61 /* AccountsBridge.swift in Sources */,

View File

@ -130,7 +130,11 @@ struct NowPlayingView: View {
}
if sections.contains(.comments) {
if !comments.loaded {
if comments.disabled {
NoCommentsView(text: "Comments are disabled", systemImage: "xmark.circle.fill")
} else if comments.loaded && comments.all.isEmpty {
NoCommentsView(text: "No comments", systemImage: "0.circle.fill")
} else if !comments.loaded {
VStack(alignment: .center) {
PlaceholderProgressView()
.onAppear {
@ -142,6 +146,14 @@ struct NowPlayingView: View {
ForEach(comments.all) { comment in
CommentView(comment: comment, repliesID: $repliesID)
}
if comments.nextPageAvailable {
Text("Scroll to load more...")
.foregroundColor(.secondary)
.padding(.leading)
.onAppear {
comments.loadNextPage()
}
}
}
}
}