feat: auto-scroll speed cycles via tab bar button (Slow/Med/Fast)
All checks were successful
Security Checks / dependency-audit (push) Successful in 13s
Security Checks / secret-scanning (push) Successful in 4s
Security Checks / dockerfile-lint (push) Successful in 4s

Tab bar action button cycles through speeds on each tap:
- ▶ (play) → tap → Slow (1.5x, hare icon)
- Slow → tap → Med (1.75x, walk icon)
- Med → tap → Fast (2.0x, bolt icon)
- Fast → tap → back to Slow

Touch feed to stop → icon returns to ▶

Removed speed controls from Reader toolbar — all speed control
is now in the single tab bar button. Clean, no overlays.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yusuf Suleman
2026-04-04 10:28:03 -05:00
parent e0ae9cb95f
commit 01c63d69d0
2 changed files with 34 additions and 31 deletions

View File

@@ -29,21 +29,41 @@ struct MainTabView: View {
@State private var confettiTrigger = 0
@State private var readerVM = ReaderViewModel()
@State private var isAutoScrolling = false
@State private var scrollSpeed: Double = 1.0
@State private var scrollSpeed: Double = 1.5
@State private var speedLevel = 0 // 0=slow, 1=med, 2=fast
@State private var previousTab = 0
private var showReader: Bool {
auth.currentUser?.id != 4
}
// Dynamic icon for the search-role tab based on context
private let speedLevels: [(String, String, Double)] = [
("Slow", "hare", 1.5),
("Med", "figure.walk", 1.75),
("Fast", "bolt.fill", 2.0),
]
// Dynamic icon + label for the search-role tab
private var actionIcon: String {
if selectedTab == 2 && isAutoScrolling {
return speedLevels[speedLevel].1
}
if selectedTab == 2 {
return isAutoScrolling ? "pause.fill" : "play.fill"
return "play.fill"
}
return "plus"
}
private var actionLabel: String {
if selectedTab == 2 && isAutoScrolling {
return speedLevels[speedLevel].0
}
if selectedTab == 2 {
return "Play"
}
return "Add"
}
var body: some View {
ZStack {
TabView(selection: $selectedTab) {
@@ -67,7 +87,7 @@ struct MainTabView: View {
Tab(value: 3, role: .search) {
Color.clear
} label: {
Label("Action", systemImage: actionIcon)
Label(actionLabel, systemImage: actionIcon)
}
}
.tint(Color.accentWarm)
@@ -119,9 +139,15 @@ struct MainTabView: View {
private func handleActionTap(from sourceTab: Int) {
if sourceTab == 2 {
// Reader: toggle auto-scroll, return to Reader
withAnimation(.spring(duration: 0.3)) {
isAutoScrolling.toggle()
if !isAutoScrolling {
// First tap: start at Slow
speedLevel = 0
scrollSpeed = speedLevels[0].2
isAutoScrolling = true
} else {
// Cycle: Slow Med Fast Slow
speedLevel = (speedLevel + 1) % speedLevels.count
scrollSpeed = speedLevels[speedLevel].2
}
selectedTab = 2
} else {

View File

@@ -24,28 +24,7 @@ struct ReaderTabView: View {
.navigationSubtitle(subtitleText)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
if isAutoScrolling {
// Auto-scroll mode: speed controls in toolbar
ToolbarItemGroup(placement: .topBarLeading) {
Button {
scrollSpeed = max(0.25, scrollSpeed - 0.25)
} label: {
Image(systemName: "minus")
}
Text(String(format: "%.2fx", scrollSpeed))
.font(.body.weight(.bold).monospacedDigit())
.foregroundStyle(Color.textPrimary)
Button {
scrollSpeed = min(3.0, scrollSpeed + 0.25)
} label: {
Image(systemName: "plus")
}
}
} else {
// Normal mode: controls
ToolbarItemGroup(placement: .topBarTrailing) {
ToolbarItemGroup(placement: .topBarTrailing) {
Button {
withAnimation(.easeInOut(duration: 0.2)) {
isCardView.toggle()
@@ -84,8 +63,6 @@ struct ReaderTabView: View {
Image(systemName: "ellipsis")
}
}
}
}
.sheet(isPresented: $showFeedSheet) {
AddFeedSheet(vm: vm)