Add pull-to-clean on page 2 — mirrors pull-to-backup on page 0

Pull zone now shown on pages 0 and 2. Labels route to
PULL/RELEASE TO CLEAN on page 2. Releasing triggers
triggerCleanViaPull() which fires the clean confirmation dialog
with the same ripple effect as the backup pull.

Co-Authored-By: Kutesir <kutesir@provoc.ug>
Co-Authored-By: Sentry <sentry@provoc.ug>
This commit is contained in:
Robin Kutesa
2026-05-21 14:12:54 +03:00
parent a877f34e7d
commit 76c1189412

View File

@@ -693,9 +693,9 @@ struct BackupView: View {
Color.clear.frame(height: 60)
}
}
// Pull zone floats above the ring on page 0 only
// Pull zone floats above the ring on pages 0 and 2
.overlay(alignment: .top) {
if ringPage == 0 {
if ringPage == 0 || ringPage == 2 {
pullZone
.offset(y: -max(0, pullProgress * 0.52))
}
@@ -705,13 +705,15 @@ struct BackupView: View {
private var pullZone: some View {
let pct = min(pullProgress / 58, 1.0)
let ready = pullProgress >= 58
let pullLabel = ringPage == 2 ? "PULL TO CLEAN" : "PULL TO BACK UP"
let releaseLabel = ringPage == 2 ? "RELEASE TO CLEAN" : "RELEASE TO BACK UP"
return VStack(spacing: 3) {
Text("")
.font(.system(size: 13))
.foregroundStyle(ready ? AppTheme.positive : AppTheme.inkTertiary)
.rotationEffect(.degrees(ready ? 180 : pct * 160))
.opacity(min(pct * 2.5, 1))
Text(ready ? "RELEASE TO BACK UP" : "PULL TO BACK UP")
Text(ready ? releaseLabel : pullLabel)
.font(.system(size: 8, weight: .regular, design: .monospaced))
.foregroundStyle(ready ? AppTheme.positive : AppTheme.inkTertiary)
.tracking(2)
@@ -820,7 +822,7 @@ struct BackupView: View {
withAnimation(.spring(response: 0.38, dampingFraction: 0.85)) { pullProgress = 0 }
if fired {
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
triggerBackupViaPull()
if ringPage == 2 { triggerCleanViaPull() } else { triggerBackupViaPull() }
}
}
}
@@ -831,6 +833,12 @@ struct BackupView: View {
if engine.job.status == .paused { engine.resume() } else { startBackup() }
}
private func triggerCleanViaPull() {
rippleScale = 0.88; rippleOpacity = 0.6
withAnimation(.easeOut(duration: 0.65)) { rippleScale = 1.35; rippleOpacity = 0 }
showCleanConfirm = true
}
private func arrowFlowOpacity(_ phase: Double) -> Double {
if phase < 0.4 { return phase / 0.4 * 0.7 }
if phase < 0.8 { return (0.8 - phase) / 0.4 * 0.7 }