feat: dark mode, mark-as-read on scroll, fix card tap targets
Dark mode: - All colors in Color+Extensions now adaptive (UIColor with traits) - Warm dark palette: dark brown canvas, brighter gold accent, warm cards - Article HTML CSS supports prefers-color-scheme: dark - Meal/macro colors unchanged (vivid on both themes) Reader fixes: - .contentShape(Rectangle()) on cards/rows — fixes tap target issues where small cards couldn't be clicked - Context menu moved from card/row to the NavigationLink wrapper so it doesn't interfere with taps - Mark as read on scroll via .onAppear on each entry - Cards no longer pass vm (cleaner, context menu handled at list level) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -213,6 +213,7 @@ struct ArticleView: View {
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
@@ -225,6 +226,13 @@ struct ArticleView: View {
|
||||
background: transparent;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { color: #ede8e1; }
|
||||
a { color: #c79e40 !important; }
|
||||
pre, code { background: #1e1c1a !important; }
|
||||
blockquote { border-left-color: #c79e40; color: #9a9590; }
|
||||
td, th { border-color: #333; }
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
|
||||
@@ -38,9 +38,19 @@ struct EntryListView: View {
|
||||
LazyVStack(spacing: 12) {
|
||||
ForEach(vm.entries) { entry in
|
||||
NavigationLink(value: entry) {
|
||||
EntryCardView(entry: entry, vm: vm)
|
||||
EntryCardView(entry: entry)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.contentShape(Rectangle())
|
||||
.onAppear {
|
||||
// Mark as read when scrolled into view (card mode)
|
||||
if !entry.isRead {
|
||||
Task { await vm.markAsRead(entry) }
|
||||
}
|
||||
}
|
||||
.contextMenu {
|
||||
entryContextMenu(entry: entry, vm: vm)
|
||||
}
|
||||
}
|
||||
|
||||
loadMoreTrigger
|
||||
@@ -57,9 +67,18 @@ struct EntryListView: View {
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(vm.entries) { entry in
|
||||
NavigationLink(value: entry) {
|
||||
EntryRowView(entry: entry, vm: vm)
|
||||
EntryRowView(entry: entry)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.contentShape(Rectangle())
|
||||
.onAppear {
|
||||
if !entry.isRead {
|
||||
Task { await vm.markAsRead(entry) }
|
||||
}
|
||||
}
|
||||
.contextMenu {
|
||||
entryContextMenu(entry: entry, vm: vm)
|
||||
}
|
||||
|
||||
Divider()
|
||||
.padding(.leading, 36)
|
||||
@@ -91,11 +110,10 @@ struct EntryListView: View {
|
||||
|
||||
struct EntryCardView: View {
|
||||
let entry: ReaderEntry
|
||||
let vm: ReaderViewModel
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
// Thumbnail — only show if available
|
||||
// Thumbnail
|
||||
if let thumbURL = entry.thumbnailURL {
|
||||
AsyncImage(url: thumbURL) { phase in
|
||||
switch phase {
|
||||
@@ -113,7 +131,6 @@ struct EntryCardView: View {
|
||||
|
||||
// Content
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
// Feed + time
|
||||
HStack(spacing: 6) {
|
||||
if !entry.isRead {
|
||||
Circle()
|
||||
@@ -135,13 +152,11 @@ struct EntryCardView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// Title
|
||||
Text(entry.displayTitle)
|
||||
.font(.subheadline.weight(entry.isRead ? .medium : .bold))
|
||||
.foregroundStyle(entry.isRead ? Color.textSecondary : Color.textPrimary)
|
||||
.lineLimit(3)
|
||||
|
||||
// Bottom row
|
||||
HStack(spacing: 8) {
|
||||
if let author = entry.author, !author.isEmpty {
|
||||
Text(author)
|
||||
@@ -168,18 +183,13 @@ struct EntryCardView: View {
|
||||
.background(Color.surfaceCard)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 14))
|
||||
.shadow(color: .black.opacity(0.04), radius: 6, y: 2)
|
||||
.contextMenu {
|
||||
entryContextMenu(entry: entry, vm: vm)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - List Row View
|
||||
|
||||
struct EntryRowView: View {
|
||||
let entry: ReaderEntry
|
||||
let vm: ReaderViewModel
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .top, spacing: 12) {
|
||||
@@ -227,7 +237,6 @@ struct EntryRowView: View {
|
||||
|
||||
Spacer()
|
||||
|
||||
// Thumbnail mini
|
||||
if let thumbURL = entry.thumbnailURL {
|
||||
AsyncImage(url: thumbURL) { phase in
|
||||
switch phase {
|
||||
@@ -250,10 +259,6 @@ struct EntryRowView: View {
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
.background(Color.canvas)
|
||||
.contextMenu {
|
||||
entryContextMenu(entry: entry, vm: vm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user