feat: auto-scroll speed cycles via tab bar button (Slow/Med/Fast)
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:
@@ -29,21 +29,41 @@ struct MainTabView: View {
|
|||||||
@State private var confettiTrigger = 0
|
@State private var confettiTrigger = 0
|
||||||
@State private var readerVM = ReaderViewModel()
|
@State private var readerVM = ReaderViewModel()
|
||||||
@State private var isAutoScrolling = false
|
@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
|
@State private var previousTab = 0
|
||||||
|
|
||||||
private var showReader: Bool {
|
private var showReader: Bool {
|
||||||
auth.currentUser?.id != 4
|
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 {
|
private var actionIcon: String {
|
||||||
|
if selectedTab == 2 && isAutoScrolling {
|
||||||
|
return speedLevels[speedLevel].1
|
||||||
|
}
|
||||||
if selectedTab == 2 {
|
if selectedTab == 2 {
|
||||||
return isAutoScrolling ? "pause.fill" : "play.fill"
|
return "play.fill"
|
||||||
}
|
}
|
||||||
return "plus"
|
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 {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
TabView(selection: $selectedTab) {
|
TabView(selection: $selectedTab) {
|
||||||
@@ -67,7 +87,7 @@ struct MainTabView: View {
|
|||||||
Tab(value: 3, role: .search) {
|
Tab(value: 3, role: .search) {
|
||||||
Color.clear
|
Color.clear
|
||||||
} label: {
|
} label: {
|
||||||
Label("Action", systemImage: actionIcon)
|
Label(actionLabel, systemImage: actionIcon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.tint(Color.accentWarm)
|
.tint(Color.accentWarm)
|
||||||
@@ -119,9 +139,15 @@ struct MainTabView: View {
|
|||||||
|
|
||||||
private func handleActionTap(from sourceTab: Int) {
|
private func handleActionTap(from sourceTab: Int) {
|
||||||
if sourceTab == 2 {
|
if sourceTab == 2 {
|
||||||
// Reader: toggle auto-scroll, return to Reader
|
if !isAutoScrolling {
|
||||||
withAnimation(.spring(duration: 0.3)) {
|
// First tap: start at Slow
|
||||||
isAutoScrolling.toggle()
|
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
|
selectedTab = 2
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -24,28 +24,7 @@ struct ReaderTabView: View {
|
|||||||
.navigationSubtitle(subtitleText)
|
.navigationSubtitle(subtitleText)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
if isAutoScrolling {
|
ToolbarItemGroup(placement: .topBarTrailing) {
|
||||||
// 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) {
|
|
||||||
Button {
|
Button {
|
||||||
withAnimation(.easeInOut(duration: 0.2)) {
|
withAnimation(.easeInOut(duration: 0.2)) {
|
||||||
isCardView.toggle()
|
isCardView.toggle()
|
||||||
@@ -84,8 +63,6 @@ struct ReaderTabView: View {
|
|||||||
Image(systemName: "ellipsis")
|
Image(systemName: "ellipsis")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $showFeedSheet) {
|
.sheet(isPresented: $showFeedSheet) {
|
||||||
AddFeedSheet(vm: vm)
|
AddFeedSheet(vm: vm)
|
||||||
|
|||||||
Reference in New Issue
Block a user