diff --git a/IceCubesActionExtension/IceCubesActionExtension.entitlements b/IceCubesActionExtension/IceCubesActionExtension.entitlements new file mode 100644 index 00000000..ee95ab7e --- /dev/null +++ b/IceCubesActionExtension/IceCubesActionExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + diff --git a/IceCubesApp.xcodeproj/project.pbxproj b/IceCubesApp.xcodeproj/project.pbxproj index 2220b050..6a6cf842 100644 --- a/IceCubesApp.xcodeproj/project.pbxproj +++ b/IceCubesApp.xcodeproj/project.pbxproj @@ -75,7 +75,6 @@ 9FAD85A0297456A100496AB1 /* Models in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD859F297456A100496AB1 /* Models */; }; 9FAD85A2297456A400496AB1 /* Env in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD85A1297456A400496AB1 /* Env */; }; 9FAD85A4297456A800496AB1 /* DesignSystem in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD85A3297456A800496AB1 /* DesignSystem */; }; - 9FAD85A8297582F100496AB1 /* QuickLookRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAD85A7297582F100496AB1 /* QuickLookRepresentable.swift */; }; 9FAD85CF2975B68900496AB1 /* SideBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAD85CE2975B68900496AB1 /* SideBarView.swift */; }; 9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAE4ACA293783B000772766 /* SettingsTab.swift */; }; 9FAE4ACE29379A5A00772766 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAE4ACD29379A5A00772766 /* KeychainSwift */; }; @@ -103,7 +102,7 @@ E9DF41FC29830FEC0003AAD2 /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9DF41FB29830FEC0003AAD2 /* UniformTypeIdentifiers.framework */; }; E9DF420129830FEC0003AAD2 /* ActionRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DF420029830FEC0003AAD2 /* ActionRequestHandler.swift */; }; E9DF420329830FEC0003AAD2 /* Action.js in Resources */ = {isa = PBXBuildFile; fileRef = E9DF420229830FEC0003AAD2 /* Action.js */; }; - E9DF420729830FEC0003AAD2 /* IceCubesActionExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = E9DF41FA29830FEC0003AAD2 /* IceCubesActionExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + E9DF420729830FEC0003AAD2 /* IceCubesActionExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = E9DF41FA29830FEC0003AAD2 /* IceCubesActionExtension.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; FA31A9AB2A66BF7C00D5F662 /* EditTagGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA31A9AA2A66BF7C00D5F662 /* EditTagGroupView.swift */; }; FAD203D02A66D8A80030A7FD /* Symbols.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAD203CF2A66D8A80030A7FD /* Symbols.swift */; }; /* End PBXBuildFile section */ @@ -212,13 +211,13 @@ 9FAD858A29743F7400496AB1 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; 9FAD858F29743F7400496AB1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9FAD859629743F7E00496AB1 /* IceCubesShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IceCubesShareExtension.entitlements; sourceTree = ""; }; - 9FAD85A7297582F100496AB1 /* QuickLookRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickLookRepresentable.swift; sourceTree = ""; }; 9FAD85CE2975B68900496AB1 /* SideBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideBarView.swift; sourceTree = ""; }; 9FAE4AC8293774FF00772766 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 9FAE4ACA293783B000772766 /* SettingsTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTab.swift; sourceTree = ""; }; 9FBFE639292A715500C250E9 /* IceCubesApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IceCubesApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 9FBFE63C292A715500C250E9 /* IceCubesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IceCubesApp.swift; sourceTree = ""; }; 9FBFE642292A715600C250E9 /* IceCubesApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IceCubesApp.entitlements; sourceTree = ""; }; + 9FC60CB82AE6C2F600C6EAD2 /* IceCubesActionExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IceCubesActionExtension.entitlements; sourceTree = ""; }; 9FCBB3D22984EFD5009B77EE /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; }; 9FCBB3D429859615009B77EE /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = ""; }; 9FD34822293D06E800DB0EE9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -343,7 +342,6 @@ 9FBFE63C292A715500C250E9 /* IceCubesApp.swift */, 9F398AA52935FE8A00A889F2 /* AppRegistry.swift */, 639CDF9B296AC82F00C35E58 /* SafariRouter.swift */, - 9FAD85A7297582F100496AB1 /* QuickLookRepresentable.swift */, 9FAD85CE2975B68900496AB1 /* SideBarView.swift */, ); path = App; @@ -517,6 +515,7 @@ E9DF41FD29830FEC0003AAD2 /* IceCubesActionExtension */ = { isa = PBXGroup; children = ( + 9FC60CB82AE6C2F600C6EAD2 /* IceCubesActionExtension.entitlements */, E9DF420029830FEC0003AAD2 /* ActionRequestHandler.swift */, E9DF420229830FEC0003AAD2 /* Action.js */, E9DF420429830FEC0003AAD2 /* Info.plist */, @@ -818,7 +817,6 @@ 9F55C68D2955968700F94077 /* ExploreTab.swift in Sources */, 9F1E8B47298EBCBB00609F80 /* HapticSettingsView.swift in Sources */, 9F2A5411296A1429009B2D7C /* PushNotificationsView.swift in Sources */, - 9FAD85A8297582F100496AB1 /* QuickLookRepresentable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -847,6 +845,7 @@ }; E9DF420629830FEC0003AAD2 /* PBXTargetDependency */ = { isa = PBXTargetDependency; + platformFilter = ios; target = E9DF41F929830FEC0003AAD2 /* IceCubesActionExtension */; targetProxy = E9DF420529830FEC0003AAD2 /* PBXContainerItemProxy */; }; @@ -881,6 +880,7 @@ CODE_SIGN_ENTITLEMENTS = IceCubesNotifications/IceCubesNotifications.entitlements; CODE_SIGN_IDENTITY = "-"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 730; DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; @@ -899,6 +899,9 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -911,6 +914,7 @@ CODE_SIGN_ENTITLEMENTS = IceCubesNotifications/IceCubesNotifications.entitlements; CODE_SIGN_IDENTITY = "-"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 730; DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; @@ -929,6 +933,9 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -942,6 +949,7 @@ CODE_SIGN_ENTITLEMENTS = IceCubesShareExtension/IceCubesShareExtension.entitlements; CODE_SIGN_IDENTITY = "-"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 730; DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; @@ -960,6 +968,9 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -972,6 +983,7 @@ CODE_SIGN_ENTITLEMENTS = IceCubesShareExtension/IceCubesShareExtension.entitlements; CODE_SIGN_IDENTITY = "-"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 730; DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; @@ -990,6 +1002,9 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1147,6 +1162,7 @@ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; INFOPLIST_KEY_NSCameraUsageDescription = "Upload photos & videos to Mastodon"; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Upload photos & videos to Mastodon"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; @@ -1166,8 +1182,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; @@ -1200,6 +1216,7 @@ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; INFOPLIST_KEY_NSCameraUsageDescription = "Upload photos & videos to Mastodon"; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Upload photos & videos to Mastodon"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; @@ -1219,8 +1236,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; @@ -1232,6 +1249,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = ActionIcon; + CODE_SIGN_ENTITLEMENTS = IceCubesActionExtension/IceCubesActionExtension.entitlements; CODE_SIGN_IDENTITY = "-"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; @@ -1253,6 +1271,9 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1263,6 +1284,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = ActionIcon; + CODE_SIGN_ENTITLEMENTS = IceCubesActionExtension/IceCubesActionExtension.entitlements; CODE_SIGN_IDENTITY = "-"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; @@ -1284,6 +1306,9 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/IceCubesApp/App/AppRegistry.swift b/IceCubesApp/App/AppRegistry.swift index f0111ee7..c8928ce1 100644 --- a/IceCubesApp/App/AppRegistry.swift +++ b/IceCubesApp/App/AppRegistry.swift @@ -180,3 +180,9 @@ struct ActivityView: UIViewControllerRepresentable { func updateUIViewController(_: UIActivityViewController, context _: UIViewControllerRepresentableContext) {} } + +extension URL: Identifiable { + public var id: String { + absoluteString + } +} diff --git a/IceCubesApp/App/IceCubesApp.swift b/IceCubesApp/App/IceCubesApp.swift index a0fd9a39..31f1fe14 100644 --- a/IceCubesApp/App/IceCubesApp.swift +++ b/IceCubesApp/App/IceCubesApp.swift @@ -37,6 +37,11 @@ struct IceCubesApp: App { } var body: some Scene { + appScene + otherScenes + } + + private var appScene: some Scene { WindowGroup { appView .applyTheme(theme) @@ -60,14 +65,8 @@ struct IceCubesApp: App { attachments: quickLook.mediaAttachments) .presentationBackground(.ultraThinMaterial) .presentationCornerRadius(16) - .environment(userPreferences) - .environment(theme) + .withEnvironments() } - .fullScreenCover(item: $quickLook.url, content: { url in - QuickLookPreview(selectedURL: url, urls: quickLook.urls) - .edgesIgnoringSafeArea(.bottom) - .background(TransparentBackground()) - }) .onChange(of: pushNotificationsService.handledNotification) { _, newValue in if newValue != nil { pushNotificationsService.handledNotification = nil @@ -99,6 +98,7 @@ struct IceCubesApp: App { watcher.watch(streams: [.user, .direct]) } } + } @ViewBuilder @@ -207,6 +207,27 @@ struct IceCubesApp: App { } .id(appAccountsManager.currentClient.id) } + + private var otherScenes: some Scene { + WindowGroup(for: WindowDestination.self) { destination in + Group { + switch destination.wrappedValue { + case let .newStatusEditor(visibility): + StatusEditorView(mode: .new(visibility: visibility)) + case let .mediaViewer(attachments, selectedAttachment): + MediaUIView(selectedAttachment: selectedAttachment, + attachments: attachments) + case .none: + EmptyView() + } + } + .withEnvironments() + .withModelContainer() + .applyTheme(theme) + } + .defaultSize(width: 600, height: 800) + .windowResizability(.automatic) + } private func setNewClientsInEnv(client: Client) { currentAccount.setClient(client: client) diff --git a/IceCubesApp/App/QuickLookRepresentable.swift b/IceCubesApp/App/QuickLookRepresentable.swift deleted file mode 100644 index c647c00b..00000000 --- a/IceCubesApp/App/QuickLookRepresentable.swift +++ /dev/null @@ -1,101 +0,0 @@ -import QuickLook -import SwiftUI -import UIKit - -extension URL: Identifiable { - public var id: String { - absoluteString - } -} - -struct QuickLookPreview: UIViewControllerRepresentable { - let selectedURL: URL - let urls: [URL] - - func makeUIViewController(context _: Context) -> UIViewController { - AppQLPreviewController(selectedURL: selectedURL, urls: urls) - } - - func updateUIViewController( - _: UIViewController, context _: Context - ) {} -} - -@MainActor -class AppQLPreviewController: UIViewController { - let selectedURL: URL - let urls: [URL] - - var qlController: QLPreviewController? - - init(selectedURL: URL, urls: [URL]) { - self.selectedURL = selectedURL - self.urls = urls - super.init(nibName: nil, bundle: nil) - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - if qlController == nil { - qlController = QLPreviewController() - qlController?.dataSource = self - qlController?.delegate = self - qlController?.currentPreviewItemIndex = urls.firstIndex(of: selectedURL) ?? 0 - present(qlController!, animated: true) - } - } -} - -extension AppQLPreviewController: QLPreviewControllerDataSource { - nonisolated func numberOfPreviewItems(in _: QLPreviewController) -> Int { - urls.count - } - - nonisolated func previewController(_: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem { - urls[index] as QLPreviewItem - } -} - -extension AppQLPreviewController: QLPreviewControllerDelegate { - nonisolated func previewController(_: QLPreviewController, editingModeFor _: QLPreviewItem) -> QLPreviewItemEditingMode { - .createCopy - } - - nonisolated func previewControllerWillDismiss(_: QLPreviewController) { - Task { @MainActor in - dismiss(animated: true) - } - } - - nonisolated func previewControllerDidDismiss(_: QLPreviewController) { - Task { @MainActor in - dismiss(animated: true) - } - } -} - -struct TransparentBackground: UIViewControllerRepresentable { - public func makeUIViewController(context _: Context) -> UIViewController { - TransparentController() - } - - public func updateUIViewController(_: UIViewController, context _: Context) {} - - class TransparentController: UIViewController { - override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = .clear - } - - override func willMove(toParent parent: UIViewController?) { - super.willMove(toParent: parent) - parent?.view?.backgroundColor = .clear - parent?.modalPresentationStyle = .overCurrentContext - } - } -} diff --git a/IceCubesApp/App/SafariRouter.swift b/IceCubesApp/App/SafariRouter.swift index 128647c6..2ba875b2 100644 --- a/IceCubesApp/App/SafariRouter.swift +++ b/IceCubesApp/App/SafariRouter.swift @@ -42,7 +42,7 @@ private struct SafariRouter: ViewModifier { return .handled } } - guard preferences.preferredBrowser == .inAppSafari, !ProcessInfo.processInfo.isiOSAppOnMac else { return .systemAction } + guard preferences.preferredBrowser == .inAppSafari, !ProcessInfo.processInfo.isMacCatalystApp else { return .systemAction } // SFSafariViewController only supports initial URLs with http:// or https:// schemes. guard let scheme = url.scheme, ["https", "http"].contains(scheme.lowercased()) else { return .systemAction diff --git a/IceCubesApp/App/SideBarView.swift b/IceCubesApp/App/SideBarView.swift index 01beb719..d966f6df 100644 --- a/IceCubesApp/App/SideBarView.swift +++ b/IceCubesApp/App/SideBarView.swift @@ -7,6 +7,8 @@ import SwiftUI @MainActor struct SideBarView: View { + @Environment(\.openWindow) private var openWindow + @Environment(AppAccountsManager.self) private var appAccounts @Environment(CurrentAccount.self) private var currentAccount @Environment(Theme.self) private var theme @@ -55,7 +57,11 @@ struct SideBarView: View { private var postButton: some View { Button { - routerPath.presentedSheet = .newStatusEditor(visibility: userPreferences.postVisibility) + if ProcessInfo.processInfo.isMacCatalystApp { + openWindow(value: WindowDestination.newStatusEditor(visibility: userPreferences.postVisibility)) + } else { + routerPath.presentedSheet = .newStatusEditor(visibility: userPreferences.postVisibility) + } } label: { Image(systemName: "square.and.pencil") .resizable() diff --git a/IceCubesApp/App/Tabs/Settings/SettingsTab.swift b/IceCubesApp/App/Tabs/Settings/SettingsTab.swift index 9b804f12..88af1e56 100644 --- a/IceCubesApp/App/Tabs/Settings/SettingsTab.swift +++ b/IceCubesApp/App/Tabs/Settings/SettingsTab.swift @@ -169,7 +169,7 @@ struct SettingsTabs: View { private var otherSections: some View { @Bindable var preferences = preferences Section("settings.section.other") { - if !ProcessInfo.processInfo.isiOSAppOnMac { + if !ProcessInfo.processInfo.isMacCatalystApp{ Picker(selection: $preferences.preferredBrowser) { ForEach(PreferredBrowser.allCases, id: \.rawValue) { browser in switch browser { @@ -202,7 +202,7 @@ struct SettingsTabs: View { private var appSection: some View { Section { - if !ProcessInfo.processInfo.isiOSAppOnMac { + if !ProcessInfo.processInfo.isMacCatalystApp { NavigationLink(destination: IconSelectorView()) { Label { Text("settings.app.icon") diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/128.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/128.png deleted file mode 100755 index 8ff599a7..00000000 Binary files a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/128.png and /dev/null differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/16.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/16.png deleted file mode 100755 index c9f41c69..00000000 Binary files a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/16.png and /dev/null differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/256.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/256.png deleted file mode 100755 index c5fdb987..00000000 Binary files a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/256.png and /dev/null differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/32.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/32.png deleted file mode 100755 index 7d20ede1..00000000 Binary files a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/32.png and /dev/null differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/512.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/512.png deleted file mode 100755 index ca8a6bd7..00000000 Binary files a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/512.png and /dev/null differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/60.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/60.png old mode 100755 new mode 100644 diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/64.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/64.png deleted file mode 100755 index ec0d2cea..00000000 Binary files a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/64.png and /dev/null differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Contents.json index 5e8372b6..41745e62 100755 --- a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,284 +1,218 @@ { - "images": [ - { - "size": "60x60", - "expected-size": "180", - "filename": "180.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "3x" - }, - { - "size": "40x40", - "expected-size": "80", - "filename": "80.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "2x" - }, - { - "size": "40x40", - "expected-size": "120", - "filename": "120.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "3x" - }, - { - "size": "60x60", - "expected-size": "120", - "filename": "120.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "2x" - }, - { - "size": "57x57", - "expected-size": "57", - "filename": "57.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "1x" - }, - { - "size": "29x29", - "expected-size": "58", - "filename": "58.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "2x" - }, - { - "size": "29x29", - "expected-size": "29", - "filename": "29.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "1x" - }, - { - "size": "29x29", - "expected-size": "87", - "filename": "87.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "3x" - }, - { - "size": "57x57", - "expected-size": "114", - "filename": "114.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "2x" - }, - { - "size": "20x20", - "expected-size": "40", - "filename": "40.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "2x" - }, - { - "size": "20x20", - "expected-size": "60", - "filename": "60.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "3x" - }, - { - "size": "1024x1024", - "filename": "1024.png", - "expected-size": "1024", - "idiom": "ios-marketing", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "scale": "1x" - }, - { - "size": "40x40", - "expected-size": "80", - "filename": "80.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "72x72", - "expected-size": "72", - "filename": "72.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "1x" - }, - { - "size": "76x76", - "expected-size": "152", - "filename": "152.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "50x50", - "expected-size": "100", - "filename": "100.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "29x29", - "expected-size": "58", - "filename": "58.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "76x76", - "expected-size": "76", - "filename": "76.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "1x" - }, - { - "size": "29x29", - "expected-size": "29", - "filename": "29.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "1x" - }, - { - "size": "50x50", - "expected-size": "50", - "filename": "50.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "1x" - }, - { - "size": "72x72", - "expected-size": "144", - "filename": "144.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "40x40", - "expected-size": "40", - "filename": "40.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "1x" - }, - { - "size": "83.5x83.5", - "expected-size": "167", - "filename": "167.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "20x20", - "expected-size": "20", - "filename": "20.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "1x" - }, - { - "size": "20x20", - "expected-size": "40", - "filename": "40.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "128x128", - "expected-size": "128", - "filename": "128.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "1x" - }, - { - "size": "256x256", - "expected-size": "256", - "filename": "256.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "1x" - }, - { - "size": "128x128", - "expected-size": "256", - "filename": "256.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "2x" - }, - { - "size": "256x256", - "expected-size": "512", - "filename": "512.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "2x" - }, - { - "size": "32x32", - "expected-size": "32", - "filename": "32.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "1x" - }, - { - "size": "512x512", - "expected-size": "512", - "filename": "512.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "1x" - }, - { - "size": "16x16", - "expected-size": "16", - "filename": "16.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "1x" - }, - { - "size": "16x16", - "expected-size": "32", - "filename": "32.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "2x" - }, - { - "size": "32x32", - "expected-size": "64", - "filename": "64.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "2x" - }, - { - "size": "512x512", - "expected-size": "1024", - "filename": "1024.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "2x" - } - ] -} \ No newline at end of file + "images" : [ + { + "filename" : "40.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "60.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "87.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "80.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "57.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "57x57" + }, + { + "filename" : "114.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "57x57" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "180.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "20.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "80.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "50.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "50x50" + }, + { + "filename" : "100.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "50x50" + }, + { + "filename" : "72.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "72x72" + }, + { + "filename" : "144.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "72x72" + }, + { + "filename" : "76.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "152.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "167.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "1024.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + }, + { + "filename" : "Icon-16.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "filename" : "Icon-32.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "filename" : "Icon-32 1.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "filename" : "Icon-64.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "filename" : "Icon-128.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "filename" : "Icon-256 1.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "filename" : "Icon-256.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "filename" : "Icon-512 1.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "filename" : "Icon-512.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "filename" : "Icon-1024.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-1024.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-1024.png new file mode 100644 index 00000000..17b11d09 Binary files /dev/null and b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-1024.png differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-128.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-128.png new file mode 100644 index 00000000..32beb5d8 Binary files /dev/null and b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-128.png differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-16.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-16.png new file mode 100644 index 00000000..5ed79738 Binary files /dev/null and b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-16.png differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-256 1.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-256 1.png new file mode 100644 index 00000000..89bc2489 Binary files /dev/null and b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-256 1.png differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-256.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-256.png new file mode 100644 index 00000000..89bc2489 Binary files /dev/null and b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-256.png differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-32 1.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-32 1.png new file mode 100644 index 00000000..05d80642 Binary files /dev/null and b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-32 1.png differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-32.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-32.png new file mode 100644 index 00000000..05d80642 Binary files /dev/null and b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-32.png differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-512 1.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-512 1.png new file mode 100644 index 00000000..be14fb1b Binary files /dev/null and b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-512 1.png differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-512.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-512.png new file mode 100644 index 00000000..be14fb1b Binary files /dev/null and b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-512.png differ diff --git a/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-64.png b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-64.png new file mode 100644 index 00000000..988e7579 Binary files /dev/null and b/IceCubesApp/Assets.xcassets/AppIcon.appiconset/Icon-64.png differ diff --git a/IceCubesApp/IceCubesApp.entitlements b/IceCubesApp/IceCubesApp.entitlements index cb44e4cc..b9a62da1 100644 --- a/IceCubesApp/IceCubesApp.entitlements +++ b/IceCubesApp/IceCubesApp.entitlements @@ -18,6 +18,8 @@ com.apple.security.files.user-selected.read-only + com.apple.security.network.client + keychain-access-groups $(AppIdentifierPrefix)$(BUNDLE_ID_PREFIX).IceCubesApp diff --git a/IceCubesNotifications/IceCubesNotifications.entitlements b/IceCubesNotifications/IceCubesNotifications.entitlements index 187bce3e..a60eec3f 100644 --- a/IceCubesNotifications/IceCubesNotifications.entitlements +++ b/IceCubesNotifications/IceCubesNotifications.entitlements @@ -2,10 +2,14 @@ + com.apple.security.app-sandbox + com.apple.security.application-groups group.$(BUNDLE_ID_PREFIX).IceCubesApp + com.apple.security.network.client + keychain-access-groups $(AppIdentifierPrefix)$(BUNDLE_ID_PREFIX).IceCubesApp diff --git a/IceCubesShareExtension/IceCubesShareExtension.entitlements b/IceCubesShareExtension/IceCubesShareExtension.entitlements index 187bce3e..a60eec3f 100644 --- a/IceCubesShareExtension/IceCubesShareExtension.entitlements +++ b/IceCubesShareExtension/IceCubesShareExtension.entitlements @@ -2,10 +2,14 @@ + com.apple.security.app-sandbox + com.apple.security.application-groups group.$(BUNDLE_ID_PREFIX).IceCubesApp + com.apple.security.network.client + keychain-access-groups $(AppIdentifierPrefix)$(BUNDLE_ID_PREFIX).IceCubesApp diff --git a/Packages/DesignSystem/Sources/DesignSystem/DesignSystem.swift b/Packages/DesignSystem/Sources/DesignSystem/DesignSystem.swift index c5df1581..803edaa5 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/DesignSystem.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/DesignSystem.swift @@ -7,6 +7,6 @@ public extension CGFloat { static let scrollToViewHeight: CGFloat = 1 static let statusColumnsSpacing: CGFloat = 8 static let secondaryColumnWidth: CGFloat = 400 - static let sidebarWidth: CGFloat = 80 + static let sidebarWidth: CGFloat = 90 static let pollBarHeight: CGFloat = 30 } diff --git a/Packages/DesignSystem/Sources/DesignSystem/Font.swift b/Packages/DesignSystem/Sources/DesignSystem/Font.swift index 31342216..011fbec9 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/Font.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/Font.swift @@ -12,7 +12,7 @@ public extension Font { private static let subheadline = onMac ? 16.0 : 15.0 private static let footnote = onMac ? 15.0 : 13.0 private static let caption = onMac ? 14.0 : 12.0 - private static let onMac = ProcessInfo.processInfo.isiOSAppOnMac + private static let onMac = ProcessInfo.processInfo.isMacCatalystApp private static func customFont(size: CGFloat, relativeTo textStyle: TextStyle) -> Font { if let chosenFont = Theme.shared.chosenFont { diff --git a/Packages/DesignSystem/Sources/DesignSystem/SceneDelegate.swift b/Packages/DesignSystem/Sources/DesignSystem/SceneDelegate.swift index ff71431a..ee32cf7f 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/SceneDelegate.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/SceneDelegate.swift @@ -14,5 +14,12 @@ import UIKit { guard let windowScene = scene as? UIWindowScene else { return } window = windowScene.keyWindow + + #if targetEnvironment(macCatalyst) + if let titlebar = windowScene.titlebar { + titlebar.titleVisibility = .hidden + titlebar.toolbar = nil + } + #endif } } diff --git a/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift b/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift index 74abfbf9..6bde7001 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift @@ -16,7 +16,7 @@ public struct AvatarView: View { case .account: return .init(width: 80, height: 80) case .status: - if ProcessInfo.processInfo.isiOSAppOnMac { + if ProcessInfo.processInfo.isMacCatalystApp { return .init(width: 48, height: 48) } return .init(width: 40, height: 40) diff --git a/Packages/DesignSystem/Sources/DesignSystem/Views/StatusEditorToolbarItem.swift b/Packages/DesignSystem/Sources/DesignSystem/Views/StatusEditorToolbarItem.swift index 52da42a8..c77d3c53 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/Views/StatusEditorToolbarItem.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/Views/StatusEditorToolbarItem.swift @@ -4,26 +4,15 @@ import SwiftUI @MainActor public extension View { - func statusEditorToolbarItem(routerPath: RouterPath, visibility: Models.Visibility) -> some ToolbarContent { - ToolbarItem(placement: .navigationBarTrailing) { - Button { - routerPath.presentedSheet = .newStatusEditor(visibility: visibility) - HapticManager.shared.fireHaptic(of: .buttonPress) - } label: { - Image(systemName: "square.and.pencil") - .accessibilityLabel("accessibility.tabs.timeline.new-post.label") - .accessibilityInputLabels([ - LocalizedStringKey("accessibility.tabs.timeline.new-post.label"), - LocalizedStringKey("accessibility.tabs.timeline.new-post.inputLabel1"), - LocalizedStringKey("accessibility.tabs.timeline.new-post.inputLabel2"), - ]) - } - } + func statusEditorToolbarItem(routerPath: RouterPath, + visibility: Models.Visibility) -> some ToolbarContent { + StatusEditorToolbarItem(visibility: visibility) } } @MainActor public struct StatusEditorToolbarItem: ToolbarContent { + @Environment(\.openWindow) private var openWindow @Environment(RouterPath.self) private var routerPath let visibility: Models.Visibility @@ -36,8 +25,12 @@ public struct StatusEditorToolbarItem: ToolbarContent { ToolbarItem(placement: .navigationBarTrailing) { Button { Task { @MainActor in - routerPath.presentedSheet = .newStatusEditor(visibility: visibility) - HapticManager.shared.fireHaptic(of: .buttonPress) + if ProcessInfo.processInfo.isMacCatalystApp { + openWindow(value: WindowDestination.newStatusEditor(visibility: visibility)) + } else { + routerPath.presentedSheet = .newStatusEditor(visibility: visibility) + HapticManager.shared.fireHaptic(of: .buttonPress) + } } } label: { Image(systemName: "square.and.pencil") diff --git a/Packages/Env/Sources/Env/QuickLook.swift b/Packages/Env/Sources/Env/QuickLook.swift index 6c7132eb..fb5a991f 100644 --- a/Packages/Env/Sources/Env/QuickLook.swift +++ b/Packages/Env/Sources/Env/QuickLook.swift @@ -1,5 +1,4 @@ import Combine -@preconcurrency import SwiftUI import Models import QuickLook @@ -8,88 +7,10 @@ import QuickLook public var selectedMediaAttachment: MediaAttachment? public var mediaAttachments: [MediaAttachment] = [] - public var url: URL? { - didSet { - if url == nil { - cleanup(urls: urls) - } - } - } - public private(set) var urls: [URL] = [] - - public init() {} public func prepareFor(selectedMediaAttachment: MediaAttachment, mediaAttachments: [MediaAttachment]) { - if ProcessInfo.processInfo.isiOSAppOnMac, let selectedURL = selectedMediaAttachment.url { - let urls = mediaAttachments.compactMap{ $0.url } - Task { - await prepareFor(urls: urls, selectedURL: selectedURL) - } - } else { - self.selectedMediaAttachment = selectedMediaAttachment - self.mediaAttachments = mediaAttachments - } - } - - private func prepareFor(urls: [URL], selectedURL: URL) async { - var transaction = Transaction(animation: .default) - transaction.disablesAnimations = true - do { - var order = 0 - let pathOrderMap = urls.reduce(into: [String: Int]()) { result, url in - result[url.lastPathComponent] = order - order += 1 - } - let paths: [URL] = try await withThrowingTaskGroup(of: URL.self, body: { group in - var paths: [URL] = [] - for url in urls { - group.addTask { - try await self.localPathFor(url: url) - } - } - for try await path in group { - paths.append(path) - } - return paths.sorted { url1, url2 in - pathOrderMap[url1.lastPathComponent] ?? 0 < pathOrderMap[url2.lastPathComponent] ?? 0 - } - }) - withTransaction(transaction) { - self.urls = paths - url = paths.first(where: { $0.lastPathComponent == selectedURL.lastPathComponent }) - } - } catch { - withTransaction(transaction) { - self.urls = [] - url = nil - } - } - } - - private var quickLookDir: URL { - try! FileManager.default.url(for: .cachesDirectory, - in: .userDomainMask, - appropriateFor: nil, - create: false) - .appending(component: "quicklook") - } - - private func localPathFor(url: URL) async throws -> URL { - try? FileManager.default.createDirectory(at: quickLookDir, withIntermediateDirectories: true) - let path = quickLookDir.appendingPathComponent(url.lastPathComponent) - - // Warning: Non-sendable type '(any URLSessionTaskDelegate)?' exiting main actor-isolated - // context in call to non-isolated instance method 'data(for:delegate:)' cannot cross actor - // boundary. - // This is on the defaulted-to-nil second parameter of `.data(from:delegate:)`. - // There is a Radar tracking this & others like it. - let data = try await URLSession.shared.data(from: url).0 - try data.write(to: path) - return path - } - - private func cleanup(urls _: [URL]) { - try? FileManager.default.removeItem(at: quickLookDir) + self.selectedMediaAttachment = selectedMediaAttachment + self.mediaAttachments = mediaAttachments } } diff --git a/Packages/Env/Sources/Env/Router.swift b/Packages/Env/Sources/Env/Router.swift index 71930353..fce330b3 100644 --- a/Packages/Env/Sources/Env/Router.swift +++ b/Packages/Env/Sources/Env/Router.swift @@ -25,6 +25,20 @@ public enum RouterDestination: Hashable { case tagsList(tags: [Tag]) } +public enum WindowDestination: Hashable, Codable { + case newStatusEditor(visibility: Models.Visibility) + case mediaViewer(attachments: [MediaAttachment], selectedAttachment: MediaAttachment) + + var initialSize: CGSize { + switch self { + case .newStatusEditor: + return .init(width: 500, height: 700) + case .mediaViewer: + return .init(width: 800, height: 600) + } + } +} + public enum SheetDestination: Identifiable { case newStatusEditor(visibility: Models.Visibility) case editStatusEditor(status: Status) diff --git a/Packages/MediaUI/Sources/MediaUI/MediaUIView.swift b/Packages/MediaUI/Sources/MediaUI/MediaUIView.swift index cb2ef98d..e46ef11b 100644 --- a/Packages/MediaUI/Sources/MediaUI/MediaUIView.swift +++ b/Packages/MediaUI/Sources/MediaUI/MediaUIView.swift @@ -71,6 +71,7 @@ public struct MediaUIView: View, @unchecked Sendable { @ToolbarContentBuilder private var toolbarView: some ToolbarContent { + #if !targetEnvironment(macCatalyst) ToolbarItem(placement: .topBarLeading) { Button { dismiss() @@ -78,6 +79,7 @@ public struct MediaUIView: View, @unchecked Sendable { Image(systemName: "xmark.circle") } } + #endif ToolbarItem(placement: .topBarTrailing) { if let url = attachments.first(where: { $0.id == scrollToId})?.url { Button { diff --git a/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift b/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift index d8b03853..9afeba8a 100644 --- a/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift +++ b/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift @@ -37,7 +37,7 @@ struct StatusEditorAccessoryView: View { } label: { Label("status.editor.photo-library", systemImage: "photo") } - if !ProcessInfo.processInfo.isiOSAppOnMac { + if !ProcessInfo.processInfo.isMacCatalystApp { Button { isCameraPickerPresented = true } label: { diff --git a/Packages/Status/Sources/Status/Editor/StatusEditorView.swift b/Packages/Status/Sources/Status/Editor/StatusEditorView.swift index 3e1db09e..89ee291a 100644 --- a/Packages/Status/Sources/Status/Editor/StatusEditorView.swift +++ b/Packages/Status/Sources/Status/Editor/StatusEditorView.swift @@ -215,7 +215,7 @@ public struct StatusEditorView: View { SoundEffectManager.shared.playSound(of: .tootSent) NotificationCenter.default.post(name: NotificationsName.shareSheetClose, object: nil) - if !viewModel.mode.isInShareExtension, !preferences.requestedReview, !ProcessInfo.processInfo.isiOSAppOnMac { + if !viewModel.mode.isInShareExtension, !preferences.requestedReview, !ProcessInfo.processInfo.isMacCatalystApp { if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene { SKStoreReviewController.requestReview(in: scene) } diff --git a/Packages/Status/Sources/Status/Editor/UITextView/Coordinator.swift b/Packages/Status/Sources/Status/Editor/UITextView/Coordinator.swift index 658b5c9b..7957584c 100644 --- a/Packages/Status/Sources/Status/Editor/UITextView/Coordinator.swift +++ b/Packages/Status/Sources/Status/Editor/UITextView/Coordinator.swift @@ -45,7 +45,7 @@ extension TextView.Representable { textView.allowsEditingTextAttributes = false textView.returnKeyType = .default textView.allowsEditingTextAttributes = true - if ProcessInfo.processInfo.isiOSAppOnMac { + if ProcessInfo.processInfo.isMacCatalystApp { textView.inlinePredictionType = .no } diff --git a/Packages/Status/Sources/Status/Row/StatusRowView.swift b/Packages/Status/Sources/Status/Row/StatusRowView.swift index 5f5e1a5c..98e75224 100644 --- a/Packages/Status/Sources/Status/Row/StatusRowView.swift +++ b/Packages/Status/Sources/Status/Row/StatusRowView.swift @@ -9,6 +9,7 @@ import SwiftUI @MainActor public struct StatusRowView: View { + @Environment(\.openWindow) private var openWindow @Environment(\.isInCaptureMode) private var isInCaptureMode: Bool @Environment(\.redactionReasons) private var reasons @Environment(\.isCompact) private var isCompact: Bool @@ -208,7 +209,12 @@ public struct StatusRowView: View { Button("accessibility.status.media-viewer-action.label") { HapticManager.shared.fireHaptic(of: .notification(.success)) let attachments = viewModel.finalStatus.mediaAttachments - quickLook.prepareFor(selectedMediaAttachment: attachments[0], mediaAttachments: attachments) + if ProcessInfo.processInfo.isMacCatalystApp { + openWindow(value: WindowDestination.mediaViewer(attachments: attachments, + selectedAttachment: attachments[0])) + } else { + quickLook.prepareFor(selectedMediaAttachment: attachments[0], mediaAttachments: attachments) + } } } diff --git a/Packages/Status/Sources/Status/Row/Subviews/StatusRowMediaPreviewView.swift b/Packages/Status/Sources/Status/Row/Subviews/StatusRowMediaPreviewView.swift index 7d398727..d7a9577f 100644 --- a/Packages/Status/Sources/Status/Row/Subviews/StatusRowMediaPreviewView.swift +++ b/Packages/Status/Sources/Status/Row/Subviews/StatusRowMediaPreviewView.swift @@ -8,6 +8,7 @@ import MediaUI @MainActor public struct StatusRowMediaPreviewView: View { + @Environment(\.openWindow) private var openWindow @Environment(\.isSecondaryColumn) private var isSecondaryColumn: Bool @Environment(\.extraLeadingInset) private var extraLeadingInset: CGFloat @Environment(\.isInCaptureMode) private var isInCaptureMode: Bool @@ -88,7 +89,12 @@ public struct StatusRowMediaPreviewView: View { if attachments.count == 1, let attachment = attachments.first { makeFeaturedImagePreview(attachment: attachment) .onTapGesture { - quickLook.prepareFor(selectedMediaAttachment: attachment, mediaAttachments: attachments) + if ProcessInfo.processInfo.isMacCatalystApp { + openWindow(value: WindowDestination.mediaViewer(attachments: attachments, + selectedAttachment: attachment)) + } else { + quickLook.prepareFor(selectedMediaAttachment: attachment, mediaAttachments: attachments) + } } .accessibilityElement(children: .ignore) .accessibilityLabel(Self.accessibilityLabel(for: attachment)) @@ -272,7 +278,12 @@ public struct StatusRowMediaPreviewView: View { // #965: do not create overlapping tappable areas, when multiple images are shown .contentShape(Rectangle()) .onTapGesture { - quickLook.prepareFor(selectedMediaAttachment: attachment, mediaAttachments: attachments) + if ProcessInfo.processInfo.isMacCatalystApp { + openWindow(value: WindowDestination.mediaViewer(attachments: attachments, + selectedAttachment: attachment)) + } else { + quickLook.prepareFor(selectedMediaAttachment: attachment, mediaAttachments: attachments) + } } .accessibilityElement(children: .ignore) .accessibilityLabel(Self.accessibilityLabel(for: attachment))