Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Ditto.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
FAFCAE7219F90BA6000F0318 /* DittoStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAFCAE7119F90BA6000F0318 /* DittoStore.swift */; };
FAFCAE7419F98CF7000F0318 /* NewItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAFCAE7319F98CF7000F0318 /* NewItemView.swift */; };
FAFD8AC81B685182000B2364 /* Ditto.xcdatamodeld in Resources */ = {isa = PBXBuildFile; fileRef = E82B6DFE1B5E120C00FDCE3A /* Ditto.xcdatamodeld */; };
FAFD8AC91B685182000B2365 /* Ditto.xcdatamodeld in Resources */ = {isa = PBXBuildFile; fileRef = E82B6DFE1B5E120C00FDCE3A /* Ditto.xcdatamodeld */; };
FAFD8ACA1B6856FA000B2364 /* DittoCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAFD8AC91B6856FA000B2364 /* DittoCategory.swift */; };
FAFD8ACB1B6856FA000B2364 /* DittoCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAFD8AC91B6856FA000B2364 /* DittoCategory.swift */; };
FAFD8ACD1B6856FA000B2364 /* DittoItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAFD8ACC1B6856FA000B2364 /* DittoItem.swift */; };
Expand Down Expand Up @@ -493,6 +494,7 @@
files = (
FA70EAB819F5A49D00960EE2 /* Images.xcassets in Resources */,
CC000001AAAA00000000001A /* Localizable.xcstrings in Resources */,
FAFD8AC91B685182000B2365 /* Ditto.xcdatamodeld in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
64 changes: 64 additions & 0 deletions Ditto/DittoListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ struct DittoListView: View {
@State private var importResult: String?
@State private var showSyncSettings = false
@State private var showKeyboardSetup = false
@State private var legacyRecoveryPreview: LegacyDataMigrator.RecoveryPreview?
@State private var legacyRecoveryResult: String?

var body: some View {
NavigationStack {
Expand Down Expand Up @@ -86,6 +88,22 @@ struct DittoListView: View {
Label("Set Up Keyboard", systemImage: KeyboardSetupStatus.hasFullAccess ? "keyboard.fill" : "keyboard")
}

if LegacyDataMigrator.hasRecoverableLegacyData {
Button {
// Show the preview confirmation if we can read the
// legacy store; otherwise fall straight to the
// attempt-and-report-result flow so the user sees
// *why* recovery failed instead of a missing menu.
if let preview = LegacyDataMigrator.previewRecoverableData() {
legacyRecoveryPreview = preview
} else {
runLegacyRecovery()
}
} label: {
Label("Recover Old Dittos", systemImage: "tray.and.arrow.down")
}
}

Button {
showSubscription = true
} label: {
Expand Down Expand Up @@ -176,6 +194,33 @@ struct DittoListView: View {
} message: {
Text(importResult ?? "")
}
.alert("Recover Old Dittos?", isPresented: .init(
get: { legacyRecoveryPreview != nil },
set: { if !$0 { legacyRecoveryPreview = nil } }
)) {
Button("Recover") {
runLegacyRecovery()
legacyRecoveryPreview = nil
}
Button("Cancel", role: .cancel) {
legacyRecoveryPreview = nil
}
} message: {
if let preview = legacyRecoveryPreview {
Text(
// swiftlint:disable:next line_length
"Found \(preview.dittoCount) dittos across \(preview.categoryCount) categories from your previous version of Ditto. Recovering will merge them into your current library; duplicates will be skipped."
)
}
}
.alert("Recovery Complete", isPresented: .init(
get: { legacyRecoveryResult != nil },
set: { if !$0 { legacyRecoveryResult = nil } }
)) {
Button("OK") { legacyRecoveryResult = nil }
} message: {
Text(legacyRecoveryResult ?? "")
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
store.loadPendingDittos()
}
Expand All @@ -200,6 +245,25 @@ struct DittoListView: View {
}
}

private func runLegacyRecovery() {
let result = LegacyDataMigrator.recoverNow(into: store.modelContext)
store.save()
switch result {
case .nothingOnDisk:
// swiftlint:disable:next line_length
legacyRecoveryResult = String(localized: "No legacy dittos were found on this device. If you had dittos in an older version, they may have been removed by an earlier 3.0 update.")
case .foundButUnreadable(let detail):
// swiftlint:disable:next line_length
legacyRecoveryResult = String(localized: "Found old data on this device, but couldn't read it. Please send a sysdiagnose so we can investigate.\n\n(\(detail))")
case .emptyStore:
legacyRecoveryResult = String(localized: "Found an old data file on this device, but it had no dittos in it.")
case .inserted(0):
legacyRecoveryResult = String(localized: "Your old dittos were already in your current library — nothing new to recover.")
case .inserted(let count):
legacyRecoveryResult = String(localized: "Recovered \(count) dittos from your previous version.")
}
}

private var categoryList: some View {
List {
ForEach(Array(store.categories.enumerated()), id: \.element.persistentModelID) { index, cat in
Expand Down
2 changes: 1 addition & 1 deletion Ditto/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.0.1</string>
<string>3.0.2</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
Expand Down
Loading
Loading