From 5049cfc7d607a1c67870e010ad1dccf63627634a Mon Sep 17 00:00:00 2001 From: Robin Kutesa Date: Fri, 22 May 2026 03:08:44 +0300 Subject: [PATCH] feat(backup): toggleable backed-up photos in clean sheet; anchor page 1 stats to bottom Backed-up photos row in clean sheet now toggles on/off like other rows. Page 1 storage stats (USED/FREE/JUNK) pushed to bottom of screen via Spacer, matching the alignment of the stats strip on the main backup page. Co-Authored-By: Kutesir Co-Authored-By: Sentry --- Features/Backup/BackupView.swift | 98 +++++++++++++++++--------------- 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/Features/Backup/BackupView.swift b/Features/Backup/BackupView.swift index 4cdb5b5..1ba5fcc 100644 --- a/Features/Backup/BackupView.swift +++ b/Features/Backup/BackupView.swift @@ -24,6 +24,7 @@ struct BackupView: View { @State private var showCleanSheet = false @State private var cleanScreenshotDays: Int? = nil @State private var cleanDeletedPhotos: Bool = false + @State private var cleanBackedUpPhotos: Bool = true @State private var deletedPhotosGB: Double = 0 @State private var screenshotsGB: Double = 0 @State private var pullProgress: CGFloat = 0 @@ -71,11 +72,25 @@ struct BackupView: View { .padding(.bottom, 8) // ─── Scrollable stats / detail ───────────────────────── - ScrollView(.vertical, showsIndicators: false) { - belowContent - .padding(.bottom, 24) + if ringPage == 0 { + ScrollView(.vertical, showsIndicators: false) { + statsStrip + .padding(.horizontal, AppTheme.hPad) + .padding(.top, 4) + Spacer(minLength: 16).frame(maxHeight: 32) + nasStatusRow + .padding(.horizontal, AppTheme.hPad) + Spacer(minLength: 8).frame(maxHeight: 20) + } + .refreshable { await triggerRefreshAndBackup() } + } else if ringPage == 1 { + Spacer() + page1BelowContent + } else { + ScrollView(.vertical, showsIndicators: false) { + page2BelowContent + } } - .refreshable { await triggerRefreshAndBackup() } } // ─── Menu button — floats above scroll content ───────────── @@ -564,24 +579,6 @@ struct BackupView: View { circleSection } - @ViewBuilder - private var belowContent: some View { - switch ringPage { - case 1: - page1BelowContent - case 2: - page2BelowContent - default: - statsStrip - .padding(.horizontal, AppTheme.hPad) - .padding(.top, 4) - Spacer(minLength: 16).frame(maxHeight: 32) - nasStatusRow - .padding(.horizontal, AppTheme.hPad) - Spacer(minLength: 8).frame(maxHeight: 20) - } - } - private func etaString(_ seconds: TimeInterval) -> String { Int(seconds) / 60 > 0 ? "\(Int(seconds) / 60)m" : "\(Int(seconds))s" } @@ -925,7 +922,7 @@ struct BackupView: View { color: AppTheme.positive) } .padding(.horizontal, AppTheme.hPad) - .padding(.bottom, 18) + .padding(.bottom, 14) // Legend HStack(spacing: 20) { @@ -935,7 +932,7 @@ struct BackupView: View { } .frame(maxWidth: .infinity) .padding(.horizontal, AppTheme.hPad) - .padding(.bottom, 36) + .padding(.bottom, 24) } @@ -1047,31 +1044,40 @@ struct BackupView: View { .padding(.horizontal, AppTheme.hPad) .padding(.bottom, 24) - // Row: backed-up photos (always included, non-toggleable) - HStack(spacing: 12) { - ZStack { - Circle().fill(AppTheme.positive.opacity(0.12)).frame(width: 36, height: 36) - Image(systemName: "photo.on.rectangle") - .font(.system(size: 14, weight: .medium)) - .foregroundStyle(AppTheme.positive) + // Row: backed-up photos (toggleable) + Button { + withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) { + cleanBackedUpPhotos.toggle() } - VStack(alignment: .leading, spacing: 2) { - Text("Backed-up photos") - .font(.system(size: 14, weight: .medium)) - .foregroundStyle(AppTheme.ink) - Text(String(format: "~%.1f GB · already safe on NAS", junkGB)) - .font(.system(size: 11)) - .foregroundStyle(AppTheme.inkTertiary) + } label: { + HStack(spacing: 12) { + ZStack { + Circle() + .fill((cleanBackedUpPhotos ? AppTheme.positive : AppTheme.inkQuaternary).opacity(0.12)) + .frame(width: 36, height: 36) + Image(systemName: "photo.on.rectangle") + .font(.system(size: 14, weight: .medium)) + .foregroundStyle(cleanBackedUpPhotos ? AppTheme.positive : AppTheme.inkTertiary) + } + VStack(alignment: .leading, spacing: 2) { + Text("Backed-up photos") + .font(.system(size: 14, weight: .medium)) + .foregroundStyle(AppTheme.ink) + Text(String(format: "~%.1f GB · already safe on NAS", junkGB)) + .font(.system(size: 11)) + .foregroundStyle(AppTheme.inkTertiary) + } + Spacer() + Image(systemName: cleanBackedUpPhotos ? "checkmark.circle.fill" : "circle") + .font(.system(size: 18)) + .foregroundStyle(cleanBackedUpPhotos ? AppTheme.positive : AppTheme.inkQuaternary) } - Spacer() - Image(systemName: "checkmark.circle.fill") - .font(.system(size: 18)) - .foregroundStyle(AppTheme.positive) + .padding(.horizontal, AppTheme.hPad) + .padding(.vertical, 14) + .background(AppTheme.surfaceSunken) + .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous)) } - .padding(.horizontal, AppTheme.hPad) - .padding(.vertical, 14) - .background(AppTheme.surfaceSunken) - .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous)) + .buttonStyle(.plain) .padding(.horizontal, AppTheme.hPad) .padding(.bottom, 12)