iOS client for Grain grain.social
ios photography atproto
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: skip imageLoaded reset when incoming story fullsize is already cached

Swiping to a pane that had already been viewed would flash blur + spinner
over the image: imageLoaded was unconditionally reset in advanceStory and
presentStories, causing a render frame where cachedFullsize appeared nil
before the synchronous cache lookup ran.

Now both sites check Nuke's memory cache for the target story before
resetting — if the fullsize is already there, imageLoaded stays true and
the timer starts immediately with no intermediate loading state.

+17 -7
+17 -7
Grain/Views/Stories/StoryViewer.swift
··· 553 553 if action == .none || action == .badge { timer.start() } 554 554 } 555 555 556 + private func isFullsizeCached(_ story: GrainStory?) -> Bool { 557 + guard let url = story.flatMap({ URL(string: $0.fullsize) }) else { return false } 558 + return ImagePipeline.shared.cache.cachedImage(for: ImageRequest(url: url)) != nil 559 + } 560 + 556 561 private func goToNext() { 557 562 guard canNavigate() else { return } 558 563 markCurrentStoryViewed() ··· 578 583 579 584 private func advanceStory(by delta: Int) { 580 585 timer.progress = 0 581 - currentStoryIndex += delta 582 - imageLoaded = false 586 + let newIndex = currentStoryIndex + delta 587 + let nextStory = stories.indices.contains(newIndex) ? stories[newIndex] : nil 588 + currentStoryIndex = newIndex 589 + if !isFullsizeCached(nextStory) { imageLoaded = false } 583 590 labelRevealed = false 584 591 showLocationCopied = false 585 592 prefetchStoryImages() ··· 782 789 } 783 790 784 791 private func presentStories(_ fetched: [GrainStory], resumeIndex: Int? = nil) { 785 - imageLoaded = false 786 - showLocationCopied = false 787 - stories = fetched 792 + let targetIndex: Int 788 793 if let resume = resumeIndex { 789 - currentStoryIndex = min(resume, max(fetched.count - 1, 0)) 794 + targetIndex = min(resume, max(fetched.count - 1, 0)) 790 795 } else { 791 796 let isOwn = fetched.first?.creator.did == auth.userDID 792 - currentStoryIndex = (unreadOnly && isOwn) ? 0 : viewedStories.firstUnviewedIndex(in: fetched) 797 + targetIndex = (unreadOnly && isOwn) ? 0 : viewedStories.firstUnviewedIndex(in: fetched) 793 798 } 799 + let targetStory = fetched.indices.contains(targetIndex) ? fetched[targetIndex] : nil 800 + if !isFullsizeCached(targetStory) { imageLoaded = false } 801 + showLocationCopied = false 802 + stories = fetched 803 + currentStoryIndex = targetIndex 794 804 labelRevealed = false 795 805 isLoadingStories = false 796 806 startTimerIfSafe()