mirror of
https://github.com/leonboe1/QuickDrop.git
synced 2026-11-11 02:51:11 +00:00
macOS: Add ability to save images/videos directly to photos app
This commit is contained in:
1 parent
4ddf39817c
commit
658a2ff28e
7 files changed
+2251
-867
No files matched your search
@@ -111,6 +111,9 @@
|
||||
EB5C383F2E38C49D0052EFD7 /* LaunchAtLogin.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB5C383E2E38C49D0052EFD7 /* LaunchAtLogin.swift */; };
|
||||
EB5C38402E38C49D0052EFD7 /* LaunchAtLogin.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB5C383E2E38C49D0052EFD7 /* LaunchAtLogin.swift */; };
|
||||
EB5C38412E38C49D0052EFD7 /* LaunchAtLogin.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB5C383E2E38C49D0052EFD7 /* LaunchAtLogin.swift */; };
|
||||
EB6E6AB42ECBA46D00E71D57 /* PhotoManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBBEE1452EC9D92F00AA7831 /* PhotoManager.swift */; };
|
||||
EB6E6B212ECBA95000E71D57 /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = EB6E6B202ECBA95000E71D57 /* InfoPlist.xcstrings */; };
|
||||
EB6E6B222ECBA95000E71D57 /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = EB6E6B202ECBA95000E71D57 /* InfoPlist.xcstrings */; };
|
||||
EB6F62D72D91F4AF00534EC2 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB6F62D62D91F4AC00534EC2 /* SettingsView.swift */; };
|
||||
EB704A822E4A7BB3008E608F /* DistributionDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB704A812E4A7BAE008E608F /* DistributionDetector.swift */; };
|
||||
EB704A832E4B10D4008E608F /* DistributionDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB704A812E4A7BAE008E608F /* DistributionDetector.swift */; };
|
||||
@@ -342,6 +345,7 @@
|
||||
EB5C37E22E377C2E0052EFD7 /* QuickDropiOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QuickDropiOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EB5C38382E38C1F60052EFD7 /* justMaybe.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = justMaybe.mp3; sourceTree = "<group>"; };
|
||||
EB5C383E2E38C49D0052EFD7 /* LaunchAtLogin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAtLogin.swift; sourceTree = "<group>"; };
|
||||
EB6E6B202ECBA95000E71D57 /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = "<group>"; };
|
||||
EB6F62D62D91F4AC00534EC2 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
EB704A812E4A7BAE008E608F /* DistributionDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistributionDetector.swift; sourceTree = "<group>"; };
|
||||
EB721F232E75AFA900F2099F /* QuickDropIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = QuickDropIcon.icon; sourceTree = "<group>"; };
|
||||
@@ -493,6 +497,7 @@
|
||||
EBC05C6B2E33B57100EECC18 /* Util */,
|
||||
EBC05C702E33B57D00EECC18 /* Plus */,
|
||||
69DA9A1329E0BF5200A442DA /* Assets.xcassets */,
|
||||
EB6E6B202ECBA95000E71D57 /* InfoPlist.xcstrings */,
|
||||
EB5C36922E34EBBD0052EFD7 /* Localizable.xcstrings */,
|
||||
69DA9A1829E0BF5200A442DA /* QuickDrop.entitlements */,
|
||||
);
|
||||
@@ -874,6 +879,7 @@
|
||||
699DEBA92AB0573200115D22 /* ShareViewController.xib in Resources */,
|
||||
EB96ED8D2D7F7EA3004B3FF1 /* Assets.xcassets in Resources */,
|
||||
EB5C383A2E38C1F60052EFD7 /* justMaybe.mp3 in Resources */,
|
||||
EB6E6B212ECBA95000E71D57 /* InfoPlist.xcstrings in Resources */,
|
||||
EB950B5A2E52065A002D68FA /* timeIsNow.mp3 in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -888,6 +894,7 @@
|
||||
EBC05C8C2E33B59E00EECC18 /* timeIsNow.mp3 in Resources */,
|
||||
EB721F252E75AFA900F2099F /* QuickDropIcon.icon in Resources */,
|
||||
EB5C38392E38C1F60052EFD7 /* justMaybe.mp3 in Resources */,
|
||||
EB6E6B222ECBA95000E71D57 /* InfoPlist.xcstrings in Resources */,
|
||||
EB7654782E525ACC006B4AA3 /* MainMenu.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -1011,6 +1018,7 @@
|
||||
EB7F54122D78E96000989564 /* TutorialView.swift in Sources */,
|
||||
EB704A822E4A7BB3008E608F /* DistributionDetector.swift in Sources */,
|
||||
EB9925812E45D53E00357055 /* sharing_enums.pb.swift in Sources */,
|
||||
EB6E6AB42ECBA46D00E71D57 /* PhotoManager.swift in Sources */,
|
||||
EB96ED8F2D7F8105004B3FF1 /* Localized.swift in Sources */,
|
||||
EB7CD1452D6FB0C300CB0CB5 /* Logger.swift in Sources */,
|
||||
EB16B77A2E59BAA1007D9792 /* AppGroup.swift in Sources */,
|
||||
@@ -1150,6 +1158,7 @@
|
||||
699DEBAF2AB0573200115D22 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
AUTOMATION_APPLE_EVENTS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
@@ -1158,9 +1167,15 @@
|
||||
CURRENT_PROJECT_VERSION = "${SHARED_BUILD}";
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CAMERA = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_LOCATION = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PHOTO_LIBRARY = NO;
|
||||
ENABLE_USER_SELECTED_FILES = readonly;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
@@ -1178,6 +1193,12 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO;
|
||||
RUNTIME_EXCEPTION_ALLOW_JIT = NO;
|
||||
RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO;
|
||||
RUNTIME_EXCEPTION_DEBUGGING_TOOL = NO;
|
||||
RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = NO;
|
||||
RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = NO;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@@ -1188,6 +1209,7 @@
|
||||
699DEBB02AB0573200115D22 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
AUTOMATION_APPLE_EVENTS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
@@ -1196,9 +1218,15 @@
|
||||
CURRENT_PROJECT_VERSION = "${SHARED_BUILD}";
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CAMERA = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_LOCATION = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PHOTO_LIBRARY = NO;
|
||||
ENABLE_USER_SELECTED_FILES = readonly;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
@@ -1216,6 +1244,12 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO;
|
||||
RUNTIME_EXCEPTION_ALLOW_JIT = NO;
|
||||
RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO;
|
||||
RUNTIME_EXCEPTION_DEBUGGING_TOOL = NO;
|
||||
RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = NO;
|
||||
RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = NO;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = EXTENSION;
|
||||
"SWIFT_ACTIVE_COMPILATION_CONDITIONS[arch=*]" = "";
|
||||
@@ -1362,6 +1396,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = QuickDropIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
AUTOMATION_APPLE_EVENTS = NO;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = QuickDrop/QuickDrop.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
@@ -1372,18 +1407,28 @@
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_FILE_ACCESS_DOWNLOADS_FOLDER = readwrite;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CAMERA = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_LOCATION = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PHOTO_LIBRARY = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PRINTING = NO;
|
||||
ENABLE_RESOURCE_ACCESS_USB = NO;
|
||||
ENABLE_USER_SELECTED_FILES = readwrite;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = QuickDrop/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = QuickDrop;
|
||||
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||
INFOPLIST_KEY_LSUIElement = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Leon Böttger";
|
||||
INFOPLIST_KEY_NSMainNibFile = MainMenu;
|
||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "The add photos permission is required to save received photos and videos to your photo library.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "The photo library permission is required to save received photos and videos to your photo library.";
|
||||
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -1396,6 +1441,12 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO;
|
||||
RUNTIME_EXCEPTION_ALLOW_JIT = NO;
|
||||
RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO;
|
||||
RUNTIME_EXCEPTION_DEBUGGING_TOOL = NO;
|
||||
RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = NO;
|
||||
RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
@@ -1408,6 +1459,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = QuickDropIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
AUTOMATION_APPLE_EVENTS = NO;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = QuickDrop/QuickDrop.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
@@ -1418,18 +1470,28 @@
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_FILE_ACCESS_DOWNLOADS_FOLDER = readwrite;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CAMERA = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_LOCATION = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PHOTO_LIBRARY = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PRINTING = NO;
|
||||
ENABLE_RESOURCE_ACCESS_USB = NO;
|
||||
ENABLE_USER_SELECTED_FILES = readwrite;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = QuickDrop/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = QuickDrop;
|
||||
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||
INFOPLIST_KEY_LSUIElement = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Leon Böttger";
|
||||
INFOPLIST_KEY_NSMainNibFile = MainMenu;
|
||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "The add photos permission is required to save received photos and videos to your photo library.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "The photo library permission is required to save received photos and videos to your photo library.";
|
||||
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -1442,6 +1504,12 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO;
|
||||
RUNTIME_EXCEPTION_ALLOW_JIT = NO;
|
||||
RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO;
|
||||
RUNTIME_EXCEPTION_DEBUGGING_TOOL = NO;
|
||||
RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = NO;
|
||||
RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+841
-152
File diff suppressed because it is too large.
Load diff
@@ -8,21 +8,29 @@
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
|
||||
|
||||
let toastViewSize = CGSize(width: 400, height: 100)
|
||||
|
||||
struct QuickDropToastView: View {
|
||||
@ObservedObject var settings = Settings.shared
|
||||
@ObservedObject var receiveModel: ReceiveModel
|
||||
public var onCancel: () -> Void = {}
|
||||
|
||||
@State var autoHider: DispatchWorkItem? = nil
|
||||
|
||||
init(receiveModel: ReceiveModel,
|
||||
onCancel: @escaping () -> Void = {}) {
|
||||
/// Cancel while receiving.
|
||||
public var onCancel: () -> Void
|
||||
|
||||
init(
|
||||
receiveModel: ReceiveModel,
|
||||
onCancel: @escaping () -> Void = {}
|
||||
) {
|
||||
self.receiveModel = receiveModel
|
||||
self.onCancel = onCancel
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
|
||||
let isDone = receiveModel.toastActions != nil
|
||||
|
||||
VStack(spacing: 12) {
|
||||
HStack(spacing: 12) {
|
||||
ZStack(alignment: .bottomLeading) {
|
||||
@@ -36,7 +44,8 @@ struct QuickDropToastView: View {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(String("QuickDrop"))
|
||||
.font(.system(size: 16, weight: .semibold))
|
||||
Text("Receiving")
|
||||
|
||||
Text(isDone ? "FileTransferCompleted" : "Receiving")
|
||||
.font(.system(size: 13))
|
||||
.foregroundColor(.secondary)
|
||||
.lineLimit(1)
|
||||
@@ -46,17 +55,54 @@ struct QuickDropToastView: View {
|
||||
Spacer(minLength: 12)
|
||||
}
|
||||
|
||||
HStack() {
|
||||
|
||||
CapsuleProgress(value: receiveModel.progress ?? 1.0)
|
||||
ZStack {
|
||||
if let actions = receiveModel.toastActions {
|
||||
HStack(spacing: 8) {
|
||||
|
||||
QuickDropToastViewButton(title: "OpenInFinder") {
|
||||
actions.openFilesAction()
|
||||
actions.closeToastAction()
|
||||
}
|
||||
|
||||
Button(action: onCancel) {
|
||||
Image(systemName: "xmark")
|
||||
.font(.system(size: 10, weight: .semibold))
|
||||
.foregroundColor(.secondary)
|
||||
.frame(width: 18, height: 18)
|
||||
if let onImportToPhotos = actions.importPhotosAction {
|
||||
QuickDropToastViewButton(title: "ImportToPhotos") {
|
||||
onImportToPhotos()
|
||||
actions.closeToastAction()
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
QuickDropToastViewButton(title: "Done", action: actions.closeToastAction)
|
||||
.onAppear {
|
||||
self.autoHider = DispatchWorkItem(block: {
|
||||
actions.closeToastAction()
|
||||
})
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
|
||||
self.autoHider?.perform()
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
self.autoHider?.cancel()
|
||||
}
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.controlSize(.small)
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
|
||||
HStack {
|
||||
CapsuleProgress(value: receiveModel.progress ?? 1.0)
|
||||
|
||||
Button(action: onCancel) {
|
||||
Image(systemName: "xmark")
|
||||
.font(.system(size: 10, weight: .semibold))
|
||||
.foregroundColor(.secondary)
|
||||
.frame(width: 18, height: 18)
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
.opacity(isDone ? 0 : 1)
|
||||
}
|
||||
}
|
||||
.padding(16)
|
||||
@@ -75,6 +121,25 @@ struct QuickDropToastView: View {
|
||||
}
|
||||
|
||||
|
||||
fileprivate struct QuickDropToastViewButton: View {
|
||||
let title: String
|
||||
let action: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Button(action: action) {
|
||||
Text(title.localized())
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 4)
|
||||
.background(
|
||||
Color.gray.opacity(0.15)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
|
||||
)
|
||||
}
|
||||
.buttonStyle(.plain) // prevents blue macOS button look
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// NSVisualEffectView wrapper for macOS 11 compatible blur/vibrancy
|
||||
struct VisualEffectView: NSViewRepresentable {
|
||||
var material: NSVisualEffectView.Material = .hudWindow
|
||||
@@ -110,7 +175,7 @@ struct CapsuleProgress: View {
|
||||
ZStack(alignment: .leading) {
|
||||
Capsule()
|
||||
.fill(track)
|
||||
Capsule() // Use a plain rectangle
|
||||
Capsule()
|
||||
.fill(fill)
|
||||
.mask(
|
||||
Capsule()
|
||||
@@ -128,20 +193,22 @@ struct CapsuleProgress: View {
|
||||
// MARK: - Preview
|
||||
struct QuickDropToastView_Previews: PreviewProvider {
|
||||
struct Demo: View {
|
||||
|
||||
@State var model = ReceiveModel(controlPlusScreen: { _ in })
|
||||
|
||||
@State var done = false
|
||||
|
||||
var body: some View {
|
||||
QuickDropToastView(
|
||||
receiveModel: model,
|
||||
onCancel: { }
|
||||
)
|
||||
.padding()
|
||||
.frame(width: toastViewSize.width, height: toastViewSize.height)
|
||||
.padding(100)
|
||||
.background(Color.white.opacity(0.2))
|
||||
.onAppear {
|
||||
model.progress = 0.01
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static var previews: some View { Demo() }
|
||||
}
|
||||
@@ -22,6 +22,7 @@ class ReceiveModel: ObservableObject, InboundAppDelegate {
|
||||
|
||||
#if os(macOS)
|
||||
@Published var progress: Double? = nil
|
||||
@Published var toastActions: ToastViewAction? = nil
|
||||
private var toastWindow: NSWindow?
|
||||
private var toastHosting: NSHostingView<QuickDropToastView>?
|
||||
#endif
|
||||
@@ -163,6 +164,7 @@ class ReceiveModel: ObservableObject, InboundAppDelegate {
|
||||
}, completion: {})
|
||||
#else
|
||||
DispatchQueue.main.async {
|
||||
self.toastActions = nil
|
||||
self.progress = averageProgress
|
||||
self.showQuickDropToast(for: connectionID)
|
||||
}
|
||||
@@ -176,8 +178,6 @@ class ReceiveModel: ObservableObject, InboundAppDelegate {
|
||||
|
||||
#if os(macOS)
|
||||
DispatchQueue.main.async {
|
||||
self.hideQuickDropToast()
|
||||
|
||||
if self.processes.isEmpty {
|
||||
self.progress = nil
|
||||
}
|
||||
@@ -200,8 +200,12 @@ class ReceiveModel: ObservableObject, InboundAppDelegate {
|
||||
|
||||
controlPlusScreen(false)
|
||||
ErrorAlertHandler.shared.showErrorAlert(for: device.name ?? "Android", error: error)
|
||||
} else {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.hideQuickDropToast()
|
||||
}
|
||||
} else {
|
||||
|
||||
#if os(iOS)
|
||||
doubleVibration()
|
||||
if !savedFiles.isEmpty {
|
||||
@@ -227,10 +231,42 @@ class ReceiveModel: ObservableObject, InboundAppDelegate {
|
||||
|
||||
#if os(macOS)
|
||||
|
||||
// Show received files if wanted
|
||||
if !savedFiles.isEmpty, Settings.shared.openFinderAfterReceiving {
|
||||
log("[SaveFilesManager] Opening \(self.downloadedFiles.count) file(s) in Finder.")
|
||||
NSWorkspace.shared.activateFileViewerSelecting(self.downloadedFiles)
|
||||
let openFilesAction = {
|
||||
if !savedFiles.isEmpty {
|
||||
log("[SaveFilesManager] Opening \(savedFiles.count) file(s) in Finder.")
|
||||
NSWorkspace.shared.activateFileViewerSelecting(savedFiles)
|
||||
}
|
||||
}
|
||||
|
||||
let hasPhotoOrVideos = PhotoManager.hasPhotosOrVideos(at: savedFiles)
|
||||
let importPhotosAction: (()->())? = hasPhotoOrVideos ? {
|
||||
Task {
|
||||
do {
|
||||
try await PhotoManager.saveMediaToPhotoLibrary(from: savedFiles)
|
||||
}
|
||||
catch {
|
||||
DispatchQueue.main.async {
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .critical
|
||||
alert.messageText = "CouldNotSaveMediaToPhotoLibrary".localized()
|
||||
alert.informativeText = error.localizedDescription
|
||||
alert.addButton(withTitle: "CloseAlert".localized())
|
||||
|
||||
alert.runModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
} : nil
|
||||
|
||||
DispatchQueue.main.async {
|
||||
withAnimation {
|
||||
self.toastActions = ToastViewAction(openFilesAction: openFilesAction, importPhotosAction: importPhotosAction, closeToastAction: self.hideQuickDropToast)
|
||||
}
|
||||
|
||||
// Show received files if wanted
|
||||
if Settings.shared.openFinderAfterReceiving {
|
||||
self.toastActions?.openFilesAction()
|
||||
}
|
||||
}
|
||||
|
||||
// If distributed directly, do not request review here as it does not work
|
||||
@@ -336,7 +372,15 @@ class ReceiveModel: ObservableObject, InboundAppDelegate {
|
||||
window.orderOut(nil)
|
||||
self.toastWindow = nil
|
||||
self.toastHosting = nil
|
||||
self.toastActions = nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
struct ToastViewAction {
|
||||
let openFilesAction: () -> ()
|
||||
let importPhotosAction: (() -> ())?
|
||||
let closeToastAction: () -> ()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -5,233 +5,11 @@
|
||||
"comment" : "Bundle display name",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"cs" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"da" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"el" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "QuickDrop"
|
||||
}
|
||||
},
|
||||
"en-AU" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-GB" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"es-419" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hu" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"id" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ms" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"nb" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pt-PT" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ro" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"th" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -239,233 +17,11 @@
|
||||
"comment" : "Bundle name",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"cs" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"da" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"el" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "QuickDropiOS"
|
||||
}
|
||||
},
|
||||
"en-AU" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-GB" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"es-419" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hu" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"id" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ms" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"nb" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pt-PT" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ro" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"th" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -473,233 +29,11 @@
|
||||
"comment" : "Copyright (human-readable)",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"cs" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"da" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"el" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-AU" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-GB" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"es-419" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hu" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"id" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ms" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"nb" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pt-PT" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ro" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"th" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5,11 +5,15 @@
|
||||
// Created by Leon Böttger on 16.11.25.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
#else
|
||||
import AppKit
|
||||
#endif
|
||||
|
||||
import Photos
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
|
||||
struct PhotoManager {
|
||||
|
||||
/// Returns if there are photos or videos among the given URLs.
|
||||
@@ -44,7 +48,7 @@ struct PhotoManager {
|
||||
let exifDate = EXIFUtils.exifOriginalDate(from: sourceURL)
|
||||
imageItems.append(ImageItem(fileURL: sourceURL, creationDate: exifDate))
|
||||
} else {
|
||||
let videoDate = await getVideoCreationDate(from: sourceURL)
|
||||
let videoDate = getVideoCreationDate(from: sourceURL)
|
||||
videoItems.append(VideoItem(fileURL: sourceURL, creationDate: videoDate))
|
||||
}
|
||||
}
|
||||
@@ -96,10 +100,16 @@ struct PhotoManager {
|
||||
|
||||
// Open the Photos app
|
||||
if openPhotosOnSuccess {
|
||||
if let url = URL(string: "photos-redirect://") {
|
||||
DispatchQueue.main.async {
|
||||
DispatchQueue.main.async {
|
||||
#if os(iOS)
|
||||
if let url = URL(string: "photos-redirect://") {
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
#else
|
||||
if let url = URL(string: "photos-navigation://album?name=recently-saved") {
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,23 +136,16 @@ struct PhotoManager {
|
||||
}
|
||||
|
||||
|
||||
private static func getVideoCreationDate(from url: URL) async -> Date? {
|
||||
private static func getVideoCreationDate(from url: URL) -> Date? {
|
||||
let asset = AVURLAsset(url: url)
|
||||
|
||||
do {
|
||||
// AVMetadataItem that typically contains an ISO‑8601 string
|
||||
guard let item = try await asset.load(.creationDate),
|
||||
let dateString = try await item.load(.stringValue) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let formatter = ISO8601DateFormatter()
|
||||
return formatter.date(from: dateString)
|
||||
}
|
||||
catch {
|
||||
log("Failed to load creation date from \(url): \(error)")
|
||||
// AVMetadataItem that typically contains an ISO‑8601 string
|
||||
guard let item = asset.creationDate,
|
||||
let dateString = item.stringValue else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let formatter = ISO8601DateFormatter()
|
||||
return formatter.date(from: dateString)
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user