diff --git a/App/Views/Images/ImageDetailView.swift b/App/Views/Images/ImageDetailView.swift index 99a3029..459756f 100644 --- a/App/Views/Images/ImageDetailView.swift +++ b/App/Views/Images/ImageDetailView.swift @@ -111,6 +111,7 @@ struct ImageDetailView: View { method: .get, authStrategy: self.imageAndMetadata.image.isPrivate ? .required : .none ).0 + ImagePipeline.shared.cache.storeCachedData(data, for: self.request!) self.isDownloadingToastPresented = false } catch { self.isDownloadingToastPresented = false diff --git a/App/Views/Images/ImageInformationView.swift b/App/Views/Images/ImageInformationView.swift index d6bce29..74f3f3c 100644 --- a/App/Views/Images/ImageInformationView.swift +++ b/App/Views/Images/ImageInformationView.swift @@ -45,8 +45,21 @@ struct ImageInformationView: View { } Toggle("Private", isOn: .constant(self.imageAndMetadata.image.isPrivate)) .disabled(true) + } + Section { Toggle("Locked", isOn: .constant(self.imageAndMetadata.image.lock.isLocked)) .disabled(true) + if self.imageAndMetadata.image.lock.isLocked, + let friendlyName = self.imageAndMetadata.image.lock.version?.friendlyName + { + LabeledContent("Version", value: friendlyName) + } + } header: { + Text("Lock") + } footer: { + if self.imageAndMetadata.image.lock.upgradable == true { + Text("New lock version available.\nUpgrade by editing this image.") + } } Section("ID") { Button(action: self.copyID) { diff --git a/App/Views/Modifiers/OpenURLModifier.swift b/App/Views/Modifiers/OpenURLModifier.swift index b215be8..80cdfd1 100644 --- a/App/Views/Modifiers/OpenURLModifier.swift +++ b/App/Views/Modifiers/OpenURLModifier.swift @@ -111,12 +111,12 @@ struct OpenURLModifier: ViewModifier { @State private var error: LocalizedAlertError? private func validateURL(url: URL) -> Bool { - if (url.host() != "iamages.jkelol111.me" || url.scheme != "iamages") && - (url.pathComponents.first != "api" || url.pathComponents.last != "embed") + if (url.host() == "iamages.jkelol111.me" || url.scheme == "iamages") && + (url.pathComponents[safe: 1] == "api" && url.pathComponents.last == "embed") { - return false + return true } - return true + return false } func body(content: Content) -> some View { @@ -152,9 +152,10 @@ struct OpenURLModifier: ViewModifier { self.isImageSheetPresented = false self.isLoadingToastPresented = true - switch url.pathComponents[safe: 1] { + switch url.pathComponents[safe: 2] { case "images": - if let id = url.pathComponents[safe: 2] { + if let id = url.pathComponents[safe: 3] { + print(id) Task { do { let image = try await self.globalViewModel.getImagePublicMetadata(id: id) @@ -175,7 +176,7 @@ struct OpenURLModifier: ViewModifier { } } case "collections": - if let id = url.pathComponents[safe: 2] { + if let id = url.pathComponents[safe: 3] { Task { do { let collection = try self.globalViewModel.jsond.decode( diff --git a/Iamages.xcodeproj/project.pbxproj b/Iamages.xcodeproj/project.pbxproj index 661d7da..5cb5667 100644 --- a/Iamages.xcodeproj/project.pbxproj +++ b/Iamages.xcodeproj/project.pbxproj @@ -1005,7 +1005,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 27; + CURRENT_PROJECT_VERSION = 29; DEVELOPMENT_TEAM = 2VZNUT7D2E; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Widgets/Info.plist; @@ -1017,7 +1017,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 4.1.0; + MARKETING_VERSION = 4.1.1; PRODUCT_BUNDLE_IDENTIFIER = me.jkelol111.Iamages.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -1039,7 +1039,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 27; + CURRENT_PROJECT_VERSION = 29; DEVELOPMENT_TEAM = 2VZNUT7D2E; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Widgets/Info.plist; @@ -1051,7 +1051,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 4.1.0; + MARKETING_VERSION = 4.1.1; PRODUCT_BUNDLE_IDENTIFIER = me.jkelol111.Iamages.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -1072,7 +1072,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 27; + CURRENT_PROJECT_VERSION = 29; DEVELOPMENT_TEAM = 2VZNUT7D2E; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = ShareExtension/Info.plist; @@ -1084,7 +1084,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 4.1.0; + MARKETING_VERSION = 4.1.1; PRODUCT_BUNDLE_IDENTIFIER = me.jkelol111.Iamages.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -1104,7 +1104,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 27; + CURRENT_PROJECT_VERSION = 29; DEVELOPMENT_TEAM = 2VZNUT7D2E; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = ShareExtension/Info.plist; @@ -1116,7 +1116,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 4.1.0; + MARKETING_VERSION = 4.1.1; PRODUCT_BUNDLE_IDENTIFIER = me.jkelol111.Iamages.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -1251,7 +1251,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 27; + CURRENT_PROJECT_VERSION = 29; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; DEVELOPMENT_TEAM = 2VZNUT7D2E; @@ -1277,7 +1277,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 4.1.0; + MARKETING_VERSION = 4.1.1; PRODUCT_BUNDLE_IDENTIFIER = me.jkelol111.Iamages; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; @@ -1301,7 +1301,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 27; + CURRENT_PROJECT_VERSION = 29; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; DEVELOPMENT_TEAM = 2VZNUT7D2E; @@ -1327,7 +1327,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 4.1.0; + MARKETING_VERSION = 4.1.1; PRODUCT_BUNDLE_IDENTIFIER = me.jkelol111.Iamages; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; diff --git a/ShareExtension/ShareView.swift b/ShareExtension/ShareView.swift index 2a1c69d..8490a9c 100644 --- a/ShareExtension/ShareView.swift +++ b/ShareExtension/ShareView.swift @@ -21,6 +21,7 @@ struct ShareView: View { @FocusState private var focusedField: Field? @State private var isLoggedIn: Bool = false + @State private var isCancelAlertPresented = false @State private var isBusy: Bool = true private func dismiss() { @@ -75,10 +76,29 @@ struct ShareView: View { .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { - Button("Cancel", action: self.dismiss) - .keyboardShortcut(.escape) - .disabled(self.uploadModel.isUploading || self.isBusy) - .tint(self.uploadModel.information.description.isEmpty ? .none : .red) + Button("Cancel") { + if !self.uploadModel.information.description.isEmpty || + self.uploadModel.information.isPrivate || + self.uploadModel.information.isLocked + { + self.isCancelAlertPresented = true + } else { + self.dismiss() + } + } + .keyboardShortcut(.escape) + .disabled(self.uploadModel.isUploading || self.isBusy) + .confirmationDialog( + "Leave without uploading?", + isPresented: self.$isCancelAlertPresented, + titleVisibility: .visible + ) { + Button("Leave", role: .destructive) { + self.dismiss() + } + } message: { + Text("This image will not be uploaded") + } } ToolbarItem(placement: .primaryAction) { Button("Upload") { @@ -98,6 +118,7 @@ struct ShareView: View { } } .tint(.orange) + .interactiveDismissDisabled() .errorAlert(error: self.$uploadModel.error) .onChange(of: self.uploadModel.information.isLocked) { isLocked in self.uploadModel.information.lockKey = "" diff --git a/ShareExtension/ShareViewController.swift b/ShareExtension/ShareViewController.swift index 02d5e0d..e60aa75 100644 --- a/ShareExtension/ShareViewController.swift +++ b/ShareExtension/ShareViewController.swift @@ -3,6 +3,10 @@ import SwiftUI class ShareViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + + // Prevent share sheet dismissal. + self.isModalInPresentation = true + let shareView = ShareView() .environment(\.extensionContext, self.extensionContext) @@ -11,6 +15,7 @@ class ShareViewController: UIViewController { self.addChild(hostingController) self.view.addSubview(hostingController.view) + // Expand hosted SwiftUI VC to sheet size. hostingController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ hostingController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), diff --git a/Shared/Models/IamagesImage.swift b/Shared/Models/IamagesImage.swift index cc697e3..403e6c1 100644 --- a/Shared/Models/IamagesImage.swift +++ b/Shared/Models/IamagesImage.swift @@ -5,6 +5,13 @@ struct IamagesImage: Codable, Identifiable, Hashable { struct Lock: Codable, Hashable { enum Version: Int, Codable { case aes128gcm_argon2 = 1 + + var friendlyName: String { + switch self { + case .aes128gcm_argon2: + return NSLocalizedString("AES-128 GCM with Argon2 derived key (\(self.rawValue))", comment: "") + } + } } var isLocked: Bool