Feed resilience, clearer empty states, and read/unread
- Don't blank the feed on refresh: keep current stories until new data arrives, and ignore benign URLError.cancelled (-999) so a network blip no longer wipes the list. Coalesce WebSocket story.created bursts into a single debounced refresh. - Empty state now distinguishes polling, couldn't-reach-server (with the error + host + Retry), offline, and genuinely-empty. - Track read stories in SwiftData (ReadStory); mark a story read when opened and drop/dim its signal stripe across the feed and Latest/Saved/Search. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -7,12 +7,14 @@ struct StoryRowView: View {
|
||||
let story: StorySummary
|
||||
/// True when the story's articles are cached in SwiftData (offline-ready).
|
||||
var isCached: Bool = false
|
||||
/// True once the story has been opened — drops the orange stripe and dims it.
|
||||
var isRead: Bool = false
|
||||
|
||||
private var sourceNames: [String] { story.sources.map(\.name) }
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .top, spacing: 0) {
|
||||
SignalStripe(score: story.signalScore)
|
||||
SignalStripe(score: story.signalScore, muted: isRead)
|
||||
|
||||
VStack(alignment: .leading, spacing: 9) {
|
||||
// Title (+ cached dot)
|
||||
@@ -51,6 +53,7 @@ struct StoryRowView: View {
|
||||
.padding(.trailing, 16)
|
||||
.padding(.top, 16)
|
||||
}
|
||||
.opacity(isRead ? 0.55 : 1)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(Color.black)
|
||||
.overlay(alignment: .bottom) {
|
||||
|
||||
Reference in New Issue
Block a user