Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - Scroll performance fixes for when there are many read posts already #66

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 41 additions & 29 deletions OpenArtemis/Views/Subreddits/SubredditFeedView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ struct SubredditFeedView: View {
let appTheme: AppThemeSettings
let textSizePreference: TextSizePreference

@State var readPostIDs: Set<String> = [] // Post IDs that are maked read, this is kept to improve scrolling performance rather than checking the CoreData for each new item

@State private var forceCompactMode: Bool = false

@State private var posts: [Post] = []
Expand Down Expand Up @@ -61,9 +63,7 @@ struct SubredditFeedView: View {
ThemedList(appTheme: appTheme, textSizePreference: textSizePreference, stripStyling: true) {
if !posts.isEmpty && searchTerm.isEmpty {
ForEach(posts, id: \.id) { post in
var isRead: Bool {
readPosts.contains(where: { $0.readPostId == post.id })
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seemed to be the bottleneck.

}
let isRead = readPostIDs.contains(post.id)
let justRead = readThisSession.contains(post.id)

var isSaved: Bool {
Expand All @@ -72,10 +72,11 @@ struct SubredditFeedView: View {

if !hideReadPosts || (!isRead || isSaved || (isRead && justRead)) {
PostFeedItemView(post: post, isRead: isRead, forceCompactMode: forceCompactMode, isSaved: isSaved, appTheme: appTheme, textSizePreference: textSizePreference, useLargeThumbnail: useLargeThumbnailForMediaPreview) {
handlePostTap(post, isRead: isRead)
handlePostTap(post)
}
.if(markReadOnScroll, transform: { postFeedItem in
postFeedItem.onScrolledOffTopOfScreen {
readPostIDs.insert(post.id)
PostUtils.shared.markRead(context: managedObjectContext, postId: post.id)
readThisSession.insert(post.id)
}
Expand Down Expand Up @@ -141,18 +142,13 @@ struct SubredditFeedView: View {
}

// MARK: - Private Methods

private func handlePostAppearance(_ postId: String) {
if !posts.isEmpty && posts.count > Int(Double(posts.count) * 0.85) {
if postId == posts[Int(Double(posts.count) * 0.85)].id {
scrapeSubreddit(lastPostAfter: lastPostAfter, sort: sortOption)
}
}
}

private func handlePostTap(_ post: Post, isRead: Bool) {
private func handlePostTap(_ post: Post) {
let isRead = readPostIDs.contains(post.id)

coordinator.path.append(PostResponse(post: post))
if !isRead {
readPostIDs.insert(post.id)

PostUtils.shared.markRead(context: managedObjectContext, postId: post.id)
if (!hideReadPostsImmediately) {
readThisSession.insert(post.id)
Expand Down Expand Up @@ -183,45 +179,61 @@ struct SubredditFeedView: View {
}
}

private func deferredTurnOffLoading() {
defer {
isLoading = false
}
}

private func scrapeSubreddit(lastPostAfter: String? = nil, sort: SortOption? = nil, searchTerm: String = "", preventListIdRefresh: Bool = false) {
self.isLoading = true
if !preventListIdRefresh { self.listIdentifier = MiscUtils.randomString(length: 4) }

if searchTerm.isEmpty {
RedditScraper.scrapeSubreddit(subreddit: subredditName, lastPostAfter: lastPostAfter, sort: sort,
trackingParamRemover: trackingParamRemover, over18: over18) { result in
defer {
isLoading = false
}

switch result {
case .success(let newPosts):
if newPosts.isEmpty && self.retryCount < 3 { // if a load fails, auto retry up to 3 times
self.retryCount += 1
self.scrapeSubreddit(lastPostAfter: lastPostAfter, sort: sort, searchTerm: searchTerm, preventListIdRefresh: preventListIdRefresh)

deferredTurnOffLoading()
} else {
self.retryCount = 0
for post in newPosts {
if !postIDs.contains(post.id) {
posts.append(post)
postIDs.insert(post.id)
DispatchQueue.global(qos: .background).async {
self.retryCount = 0

var newReadIds: Set<String> = []
newPosts.forEach { post in
let isRead = readPosts.contains(where: { $0.readPostId == post.id })
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do the "slow" check on load, off the main thread.

if isRead {
newReadIds.insert(post.id)
}
}

if let newLastPost = newPosts.last {
self.lastPostAfter = newLastPost.id
}

DispatchQueue.main.async {
posts += newPosts
readPostIDs.formUnion(newReadIds)

deferredTurnOffLoading()
}
}

if let newLastPost = newPosts.last {
self.lastPostAfter = newLastPost.id
}
}
case .failure(let error):
deferredTurnOffLoading()

print("Error: \(error.localizedDescription)")
}
}
} else {
RedditScraper.search(query: searchTerm, searchType: "", sortBy: selectedSearchSortOption, topSortBy: selectedSearchTopOption,
trackingParamRemover: trackingParamRemover, over18: over18) { result in
defer {
isLoading = false
}
deferredTurnOffLoading()

switch result {
case .success(let newMedia):
Expand Down