iOS 1.2.1 - Better auto sync and sync indicator
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import Combine
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
@MainActor
|
||||
class SyncService: ObservableObject {
|
||||
@@ -455,7 +454,7 @@ class SyncService: ObservableObject {
|
||||
let zipData = try createMinimalZipFromBackup(updatedBackup)
|
||||
|
||||
// Use existing import method which properly handles data restoration
|
||||
try dataManager.importData(from: zipData)
|
||||
try dataManager.importData(from: zipData, showSuccessMessage: false)
|
||||
|
||||
// Update local data state to match imported data timestamp
|
||||
DataStateManager.shared.setLastModified(backup.exportedAt)
|
||||
@@ -735,180 +734,29 @@ class SyncService: ObservableObject {
|
||||
}
|
||||
|
||||
func triggerAutoSync(dataManager: ClimbingDataManager) {
|
||||
guard isConnected && isConfigured && isAutoSyncEnabled else { return }
|
||||
// Early exit if sync cannot proceed - don't set isSyncing
|
||||
guard isConnected && isConfigured && isAutoSyncEnabled else {
|
||||
// Ensure isSyncing is false when sync is not possible
|
||||
if isSyncing {
|
||||
isSyncing = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Prevent multiple simultaneous syncs
|
||||
guard !isSyncing else {
|
||||
return
|
||||
}
|
||||
|
||||
Task {
|
||||
do {
|
||||
try await syncWithServer(dataManager: dataManager)
|
||||
} catch {
|
||||
print("Auto-sync failed: \(error)")
|
||||
// Don't show UI errors for auto-sync failures
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DEPRECATED: Complex merge logic replaced with simple timestamp-based sync
|
||||
// These methods are no longer used but kept for reference
|
||||
@available(*, deprecated, message: "Use simple timestamp-based sync instead")
|
||||
private func performIntelligentMerge(local: ClimbDataBackup, server: ClimbDataBackup) throws
|
||||
-> ClimbDataBackup
|
||||
{
|
||||
print("Merging data - preserving all entities to prevent data loss")
|
||||
|
||||
// Merge gyms by ID, keeping most recently updated
|
||||
let mergedGyms = mergeGyms(local: local.gyms, server: server.gyms)
|
||||
|
||||
// Merge problems by ID, keeping most recently updated
|
||||
let mergedProblems = mergeProblems(local: local.problems, server: server.problems)
|
||||
|
||||
// Merge sessions by ID, keeping most recently updated
|
||||
let mergedSessions = mergeSessions(local: local.sessions, server: server.sessions)
|
||||
|
||||
// Merge attempts by ID, keeping most recently updated
|
||||
let mergedAttempts = mergeAttempts(local: local.attempts, server: server.attempts)
|
||||
|
||||
print(
|
||||
"Merge results: gyms=\(mergedGyms.count), problems=\(mergedProblems.count), sessions=\(mergedSessions.count), attempts=\(mergedAttempts.count)"
|
||||
)
|
||||
|
||||
return ClimbDataBackup(
|
||||
exportedAt: ISO8601DateFormatter().string(from: Date()),
|
||||
version: "2.0",
|
||||
formatVersion: "2.0",
|
||||
gyms: mergedGyms,
|
||||
problems: mergedProblems,
|
||||
sessions: mergedSessions,
|
||||
attempts: mergedAttempts
|
||||
)
|
||||
}
|
||||
|
||||
private func mergeGyms(local: [BackupGym], server: [BackupGym]) -> [BackupGym] {
|
||||
var merged: [String: BackupGym] = [:]
|
||||
|
||||
// Add all local gyms
|
||||
for gym in local {
|
||||
merged[gym.id] = gym
|
||||
}
|
||||
|
||||
// Add server gyms, replacing if newer
|
||||
for serverGym in server {
|
||||
if let localGym = merged[serverGym.id] {
|
||||
// Keep the most recently updated
|
||||
if isNewerThan(serverGym.updatedAt, localGym.updatedAt) {
|
||||
merged[serverGym.id] = serverGym
|
||||
await MainActor.run {
|
||||
self.isSyncing = false
|
||||
}
|
||||
} else {
|
||||
// New gym from server
|
||||
merged[serverGym.id] = serverGym
|
||||
}
|
||||
}
|
||||
|
||||
return Array(merged.values)
|
||||
}
|
||||
|
||||
private func mergeProblems(local: [BackupProblem], server: [BackupProblem]) -> [BackupProblem] {
|
||||
var merged: [String: BackupProblem] = [:]
|
||||
|
||||
// Add all local problems
|
||||
for problem in local {
|
||||
merged[problem.id] = problem
|
||||
}
|
||||
|
||||
// Add server problems, replacing if newer or merging image paths
|
||||
for serverProblem in server {
|
||||
if let localProblem = merged[serverProblem.id] {
|
||||
// Merge image paths from both sources
|
||||
let localImages = Set(localProblem.imagePaths ?? [])
|
||||
let serverImages = Set(serverProblem.imagePaths ?? [])
|
||||
let mergedImages = Array(localImages.union(serverImages))
|
||||
|
||||
// Use most recently updated problem data but with merged images
|
||||
let newerProblem =
|
||||
isNewerThan(serverProblem.updatedAt, localProblem.updatedAt)
|
||||
? serverProblem : localProblem
|
||||
merged[serverProblem.id] = BackupProblem(
|
||||
id: newerProblem.id,
|
||||
gymId: newerProblem.gymId,
|
||||
name: newerProblem.name,
|
||||
description: newerProblem.description,
|
||||
climbType: newerProblem.climbType,
|
||||
difficulty: newerProblem.difficulty,
|
||||
tags: newerProblem.tags,
|
||||
location: newerProblem.location,
|
||||
imagePaths: mergedImages.isEmpty ? nil : mergedImages,
|
||||
isActive: newerProblem.isActive,
|
||||
dateSet: newerProblem.dateSet,
|
||||
notes: newerProblem.notes,
|
||||
createdAt: newerProblem.createdAt,
|
||||
updatedAt: newerProblem.updatedAt
|
||||
)
|
||||
} else {
|
||||
// New problem from server
|
||||
merged[serverProblem.id] = serverProblem
|
||||
}
|
||||
}
|
||||
|
||||
return Array(merged.values)
|
||||
}
|
||||
|
||||
private func mergeSessions(local: [BackupClimbSession], server: [BackupClimbSession])
|
||||
-> [BackupClimbSession]
|
||||
{
|
||||
var merged: [String: BackupClimbSession] = [:]
|
||||
|
||||
// Add all local sessions
|
||||
for session in local {
|
||||
merged[session.id] = session
|
||||
}
|
||||
|
||||
// Add server sessions, replacing if newer
|
||||
for serverSession in server {
|
||||
if let localSession = merged[serverSession.id] {
|
||||
// Keep the most recently updated
|
||||
if isNewerThan(serverSession.updatedAt, localSession.updatedAt) {
|
||||
merged[serverSession.id] = serverSession
|
||||
}
|
||||
} else {
|
||||
// New session from server
|
||||
merged[serverSession.id] = serverSession
|
||||
}
|
||||
}
|
||||
|
||||
return Array(merged.values)
|
||||
}
|
||||
|
||||
private func mergeAttempts(local: [BackupAttempt], server: [BackupAttempt]) -> [BackupAttempt] {
|
||||
var merged: [String: BackupAttempt] = [:]
|
||||
|
||||
// Add all local attempts
|
||||
for attempt in local {
|
||||
merged[attempt.id] = attempt
|
||||
}
|
||||
|
||||
// Add server attempts, replacing if newer
|
||||
for serverAttempt in server {
|
||||
if let localAttempt = merged[serverAttempt.id] {
|
||||
// Keep the most recently created (attempts don't typically get updated)
|
||||
if isNewerThan(serverAttempt.createdAt, localAttempt.createdAt) {
|
||||
merged[serverAttempt.id] = serverAttempt
|
||||
}
|
||||
} else {
|
||||
// New attempt from server
|
||||
merged[serverAttempt.id] = serverAttempt
|
||||
}
|
||||
}
|
||||
|
||||
return Array(merged.values)
|
||||
}
|
||||
|
||||
private func isNewerThan(_ dateString1: String, _ dateString2: String) -> Bool {
|
||||
let formatter = ISO8601DateFormatter()
|
||||
guard let date1 = formatter.date(from: dateString1),
|
||||
let date2 = formatter.date(from: dateString2)
|
||||
else {
|
||||
return false
|
||||
}
|
||||
return date1 > date2
|
||||
}
|
||||
|
||||
func disconnect() {
|
||||
@@ -931,8 +779,6 @@ class SyncService: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
// Removed SyncTrigger enum - now using simple auto sync on any data change
|
||||
|
||||
enum SyncError: LocalizedError {
|
||||
case notConfigured
|
||||
case notConnected
|
||||
|
||||
Reference in New Issue
Block a user