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 <kutesir@provoc.ug> Co-Authored-By: Sentry <sentry@provoc.ug>
This commit is contained in:
@@ -24,6 +24,7 @@ struct BackupView: View {
|
|||||||
@State private var showCleanSheet = false
|
@State private var showCleanSheet = false
|
||||||
@State private var cleanScreenshotDays: Int? = nil
|
@State private var cleanScreenshotDays: Int? = nil
|
||||||
@State private var cleanDeletedPhotos: Bool = false
|
@State private var cleanDeletedPhotos: Bool = false
|
||||||
|
@State private var cleanBackedUpPhotos: Bool = true
|
||||||
@State private var deletedPhotosGB: Double = 0
|
@State private var deletedPhotosGB: Double = 0
|
||||||
@State private var screenshotsGB: Double = 0
|
@State private var screenshotsGB: Double = 0
|
||||||
@State private var pullProgress: CGFloat = 0
|
@State private var pullProgress: CGFloat = 0
|
||||||
@@ -71,11 +72,25 @@ struct BackupView: View {
|
|||||||
.padding(.bottom, 8)
|
.padding(.bottom, 8)
|
||||||
|
|
||||||
// ─── Scrollable stats / detail ─────────────────────────
|
// ─── Scrollable stats / detail ─────────────────────────
|
||||||
ScrollView(.vertical, showsIndicators: false) {
|
if ringPage == 0 {
|
||||||
belowContent
|
ScrollView(.vertical, showsIndicators: false) {
|
||||||
.padding(.bottom, 24)
|
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 ─────────────
|
// ─── Menu button — floats above scroll content ─────────────
|
||||||
@@ -564,24 +579,6 @@ struct BackupView: View {
|
|||||||
circleSection
|
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 {
|
private func etaString(_ seconds: TimeInterval) -> String {
|
||||||
Int(seconds) / 60 > 0 ? "\(Int(seconds) / 60)m" : "\(Int(seconds))s"
|
Int(seconds) / 60 > 0 ? "\(Int(seconds) / 60)m" : "\(Int(seconds))s"
|
||||||
}
|
}
|
||||||
@@ -925,7 +922,7 @@ struct BackupView: View {
|
|||||||
color: AppTheme.positive)
|
color: AppTheme.positive)
|
||||||
}
|
}
|
||||||
.padding(.horizontal, AppTheme.hPad)
|
.padding(.horizontal, AppTheme.hPad)
|
||||||
.padding(.bottom, 18)
|
.padding(.bottom, 14)
|
||||||
|
|
||||||
// Legend
|
// Legend
|
||||||
HStack(spacing: 20) {
|
HStack(spacing: 20) {
|
||||||
@@ -935,7 +932,7 @@ struct BackupView: View {
|
|||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding(.horizontal, AppTheme.hPad)
|
.padding(.horizontal, AppTheme.hPad)
|
||||||
.padding(.bottom, 36)
|
.padding(.bottom, 24)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1047,31 +1044,40 @@ struct BackupView: View {
|
|||||||
.padding(.horizontal, AppTheme.hPad)
|
.padding(.horizontal, AppTheme.hPad)
|
||||||
.padding(.bottom, 24)
|
.padding(.bottom, 24)
|
||||||
|
|
||||||
// Row: backed-up photos (always included, non-toggleable)
|
// Row: backed-up photos (toggleable)
|
||||||
HStack(spacing: 12) {
|
Button {
|
||||||
ZStack {
|
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
|
||||||
Circle().fill(AppTheme.positive.opacity(0.12)).frame(width: 36, height: 36)
|
cleanBackedUpPhotos.toggle()
|
||||||
Image(systemName: "photo.on.rectangle")
|
|
||||||
.font(.system(size: 14, weight: .medium))
|
|
||||||
.foregroundStyle(AppTheme.positive)
|
|
||||||
}
|
}
|
||||||
VStack(alignment: .leading, spacing: 2) {
|
} label: {
|
||||||
Text("Backed-up photos")
|
HStack(spacing: 12) {
|
||||||
.font(.system(size: 14, weight: .medium))
|
ZStack {
|
||||||
.foregroundStyle(AppTheme.ink)
|
Circle()
|
||||||
Text(String(format: "~%.1f GB · already safe on NAS", junkGB))
|
.fill((cleanBackedUpPhotos ? AppTheme.positive : AppTheme.inkQuaternary).opacity(0.12))
|
||||||
.font(.system(size: 11))
|
.frame(width: 36, height: 36)
|
||||||
.foregroundStyle(AppTheme.inkTertiary)
|
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()
|
.padding(.horizontal, AppTheme.hPad)
|
||||||
Image(systemName: "checkmark.circle.fill")
|
.padding(.vertical, 14)
|
||||||
.font(.system(size: 18))
|
.background(AppTheme.surfaceSunken)
|
||||||
.foregroundStyle(AppTheme.positive)
|
.clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
|
||||||
}
|
}
|
||||||
.padding(.horizontal, AppTheme.hPad)
|
.buttonStyle(.plain)
|
||||||
.padding(.vertical, 14)
|
|
||||||
.background(AppTheme.surfaceSunken)
|
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
|
|
||||||
.padding(.horizontal, AppTheme.hPad)
|
.padding(.horizontal, AppTheme.hPad)
|
||||||
.padding(.bottom, 12)
|
.padding(.bottom, 12)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user