Split save/mark-read swipe; move read stories into a collapsible shelf
Some checks failed
CI / Build · macos-14 · Debug (push) Has been cancelled
CI / Build · macos-15 · Debug (push) Has been cancelled
CI / Build · macos-14 · Release (push) Has been cancelled
CI / Build · macos-15 · Release (push) Has been cancelled

- Left swipe: short reveals Save (tap), full swipe marks read. Right swipe
  still shares.
- Read stories leave the main feed and collapse into a "Read" shelf at the
  bottom (collapsed by default), shown as slim one-line rows; swipe to mark
  unread. An "all caught up" state shows when everything loaded is read.
- StoryRowView gains a compact presentation for the shelf.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Robin Kutesa
2026-06-19 15:07:58 +03:00
parent feee58748c
commit 4331621ccc
2 changed files with 169 additions and 67 deletions

View File

@@ -9,10 +9,37 @@ struct StoryRowView: View {
var isCached: Bool = false
/// True once the story has been opened drops the orange stripe and dims it.
var isRead: Bool = false
/// Slim single-line presentation, used for the "Read" shelf.
var compact: Bool = false
private var sourceNames: [String] { story.sources.map(\.name) }
var body: some View {
if compact { compactBody } else { fullBody }
}
private var compactBody: some View {
HStack(spacing: 0) {
SignalStripe(score: story.signalScore, muted: true)
Text(story.headline)
.font(.system(size: 14, weight: .bold))
.foregroundStyle(Color(hex: "6A6A6A"))
.lineLimit(1)
.padding(.leading, 14)
.padding(.vertical, 11)
Spacer(minLength: 12)
Text("\(story.signalScore)")
.font(.system(size: 14, weight: .heavy, design: .monospaced))
.foregroundStyle(Color(hex: "474747"))
.padding(.trailing, 16)
}
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color.black)
.overlay(alignment: .bottom) { Rectangle().fill(Palette.hairline).frame(height: 0.5) }
.contentShape(Rectangle())
}
private var fullBody: some View {
HStack(alignment: .top, spacing: 0) {
SignalStripe(score: story.signalScore, muted: isRead)