Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
import Foundation
|
|
|
|
|
import Photos
|
|
|
|
|
|
|
|
|
|
@MainActor
|
|
|
|
|
final class BackupViewModel: ObservableObject {
|
|
|
|
|
@Published var totalPhotoCount: Int = 0
|
Fix AppMenuView Connection Status UX and add smart sync dashboard
- Wi-Fi row: title always "Wi-Fi", subtitle shows SSID or Cellular/Not connected;
badge is Connected (green) / Cellular (amber) / Offline (muted) — no more
contradictory "No Wi-Fi" title with "Wi-Fi connected" subtitle
- NAS badge: Reachable now amber (TCP ping only, not auth-confirmed), Offline red,
Unknown muted — removes misleading green on unverified NAS connectivity
- Error rows are now tappable; chevron affordance + ScaleButtonStyle; opens detail
sheet with date, counts, duration, NAS host, trigger type, and suggested fix steps
- Sync Activity section: Clear button with confirmation alert (warns about error count
if errors exist), calls store.clearHistory()
- BackupViewModel: compareWithNAS() computes accurate pre-scan alreadySafeCount /
notBackedUpCount by matching lowercased filenames against NAS directory listing
- BackupView: ring shows state-based content (idle: not-backed-up count, running: %,
completed: checkmark), pill-style page indicator, display counts update live during
backup run, loadNASAndCompare() called on appear
Co-Authored-By: Kutesir <kutesir@provoc.ug>
Co-Authored-By: Sentry <sentry@provoc.ug>
2026-05-17 11:54:43 +03:00
|
|
|
@Published var alreadySafeCount: Int = 0
|
|
|
|
|
@Published var notBackedUpCount: Int = 0
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
@Published var photosAuthStatus: PHAuthorizationStatus = PHPhotoLibrary.authorizationStatus(for: .readWrite)
|
|
|
|
|
|
|
|
|
|
private let photoService = PhotoLibraryService()
|
Fix AppMenuView Connection Status UX and add smart sync dashboard
- Wi-Fi row: title always "Wi-Fi", subtitle shows SSID or Cellular/Not connected;
badge is Connected (green) / Cellular (amber) / Offline (muted) — no more
contradictory "No Wi-Fi" title with "Wi-Fi connected" subtitle
- NAS badge: Reachable now amber (TCP ping only, not auth-confirmed), Offline red,
Unknown muted — removes misleading green on unverified NAS connectivity
- Error rows are now tappable; chevron affordance + ScaleButtonStyle; opens detail
sheet with date, counts, duration, NAS host, trigger type, and suggested fix steps
- Sync Activity section: Clear button with confirmation alert (warns about error count
if errors exist), calls store.clearHistory()
- BackupViewModel: compareWithNAS() computes accurate pre-scan alreadySafeCount /
notBackedUpCount by matching lowercased filenames against NAS directory listing
- BackupView: ring shows state-based content (idle: not-backed-up count, running: %,
completed: checkmark), pill-style page indicator, display counts update live during
backup run, loadNASAndCompare() called on appear
Co-Authored-By: Kutesir <kutesir@provoc.ug>
Co-Authored-By: Sentry <sentry@provoc.ug>
2026-05-17 11:54:43 +03:00
|
|
|
private var fetchedAssets: [PhotoAsset] = []
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
|
|
|
|
|
func loadPhotoCount(filter: BackupFilter) {
|
|
|
|
|
let assets = photoService.fetchAssets(filter: filter)
|
Fix AppMenuView Connection Status UX and add smart sync dashboard
- Wi-Fi row: title always "Wi-Fi", subtitle shows SSID or Cellular/Not connected;
badge is Connected (green) / Cellular (amber) / Offline (muted) — no more
contradictory "No Wi-Fi" title with "Wi-Fi connected" subtitle
- NAS badge: Reachable now amber (TCP ping only, not auth-confirmed), Offline red,
Unknown muted — removes misleading green on unverified NAS connectivity
- Error rows are now tappable; chevron affordance + ScaleButtonStyle; opens detail
sheet with date, counts, duration, NAS host, trigger type, and suggested fix steps
- Sync Activity section: Clear button with confirmation alert (warns about error count
if errors exist), calls store.clearHistory()
- BackupViewModel: compareWithNAS() computes accurate pre-scan alreadySafeCount /
notBackedUpCount by matching lowercased filenames against NAS directory listing
- BackupView: ring shows state-based content (idle: not-backed-up count, running: %,
completed: checkmark), pill-style page indicator, display counts update live during
backup run, loadNASAndCompare() called on appear
Co-Authored-By: Kutesir <kutesir@provoc.ug>
Co-Authored-By: Sentry <sentry@provoc.ug>
2026-05-17 11:54:43 +03:00
|
|
|
fetchedAssets = assets
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
totalPhotoCount = assets.count
|
Fix AppMenuView Connection Status UX and add smart sync dashboard
- Wi-Fi row: title always "Wi-Fi", subtitle shows SSID or Cellular/Not connected;
badge is Connected (green) / Cellular (amber) / Offline (muted) — no more
contradictory "No Wi-Fi" title with "Wi-Fi connected" subtitle
- NAS badge: Reachable now amber (TCP ping only, not auth-confirmed), Offline red,
Unknown muted — removes misleading green on unverified NAS connectivity
- Error rows are now tappable; chevron affordance + ScaleButtonStyle; opens detail
sheet with date, counts, duration, NAS host, trigger type, and suggested fix steps
- Sync Activity section: Clear button with confirmation alert (warns about error count
if errors exist), calls store.clearHistory()
- BackupViewModel: compareWithNAS() computes accurate pre-scan alreadySafeCount /
notBackedUpCount by matching lowercased filenames against NAS directory listing
- BackupView: ring shows state-based content (idle: not-backed-up count, running: %,
completed: checkmark), pill-style page indicator, display counts update live during
backup run, loadNASAndCompare() called on appear
Co-Authored-By: Kutesir <kutesir@provoc.ug>
Co-Authored-By: Sentry <sentry@provoc.ug>
2026-05-17 11:54:43 +03:00
|
|
|
// Conservative initial state until NAS scan completes
|
|
|
|
|
alreadySafeCount = 0
|
|
|
|
|
notBackedUpCount = assets.count
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Called after NAS directory listing loads.
|
|
|
|
|
// Matches by lowercased filename — same heuristic BackupEngine uses for skip checks.
|
|
|
|
|
// This gives an accurate pre-scan "Already Safe / Not Backed Up" split before
|
|
|
|
|
// the user ever taps Start Backup.
|
|
|
|
|
func compareWithNAS(nasItems: [NASItem]) {
|
|
|
|
|
let nasFilenames = Set(
|
|
|
|
|
nasItems
|
|
|
|
|
.filter { !$0.isDirectory }
|
|
|
|
|
.map { $0.name.lowercased() }
|
|
|
|
|
)
|
|
|
|
|
var safe = 0
|
|
|
|
|
for asset in fetchedAssets {
|
|
|
|
|
if nasFilenames.contains(asset.filename.lowercased()) {
|
|
|
|
|
safe += 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
alreadySafeCount = safe
|
|
|
|
|
notBackedUpCount = totalPhotoCount - safe
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func requestPhotosAccess() async {
|
|
|
|
|
photosAuthStatus = await photoService.requestAuthorization()
|
|
|
|
|
}
|
|
|
|
|
}
|