diff --git a/IceCubesApp.xcodeproj/project.pbxproj b/IceCubesApp.xcodeproj/project.pbxproj index 3d62af3a..d628e4d7 100644 --- a/IceCubesApp.xcodeproj/project.pbxproj +++ b/IceCubesApp.xcodeproj/project.pbxproj @@ -67,7 +67,7 @@ 9FAD85832971BF7200496AB1 /* Secret.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9FAD85822971BF7200496AB1 /* Secret.plist */; }; 9FAD858B29743F7400496AB1 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAD858A29743F7400496AB1 /* ShareViewController.swift */; }; 9FAD858E29743F7400496AB1 /* (null) in Resources */ = {isa = PBXBuildFile; }; - 9FAD859229743F7400496AB1 /* IceCubesShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 9FAD858829743F7400496AB1 /* IceCubesShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 9FAD859229743F7400496AB1 /* IceCubesShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 9FAD858829743F7400496AB1 /* IceCubesShareExtension.appex */; platformFilters = (ios, maccatalyst, ); settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 9FAD85982974405D00496AB1 /* Status in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD85972974405D00496AB1 /* Status */; }; 9FAD859A297440CB00496AB1 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD8599297440CB00496AB1 /* KeychainSwift */; }; 9FAD859C2974422700496AB1 /* AppAccount in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD859B2974422700496AB1 /* AppAccount */; }; @@ -90,7 +90,7 @@ 9FD542E72962D2FF0045321A /* Lists in Frameworks */ = {isa = PBXBuildFile; productRef = 9FD542E62962D2FF0045321A /* Lists */; }; 9FE151A6293C90F900E9683D /* IconSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE151A5293C90F900E9683D /* IconSelectorView.swift */; }; 9FE3DB57296FEFCA00628CB0 /* AppAccount in Frameworks */ = {isa = PBXBuildFile; productRef = 9FE3DB56296FEFCA00628CB0 /* AppAccount */; }; - 9FF614472B2EDCE500F7B0E6 /* GiphyUISDK in Frameworks */ = {isa = PBXBuildFile; productRef = 9FF614462B2EDCE500F7B0E6 /* GiphyUISDK */; }; + 9FF614472B2EDCE500F7B0E6 /* GiphyUISDK in Frameworks */ = {isa = PBXBuildFile; platformFilters = (ios, maccatalyst, ); productRef = 9FF614462B2EDCE500F7B0E6 /* GiphyUISDK */; }; 9FF614492B2EDCEC00F7B0E6 /* GiphyUISDK in Frameworks */ = {isa = PBXBuildFile; productRef = 9FF614482B2EDCEC00F7B0E6 /* GiphyUISDK */; }; 9FFF677C299B7B2C00FE700A /* Notifications in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF677B299B7B2C00FE700A /* Notifications */; }; 9FFF677E299B7D2800FE700A /* Status in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF677D299B7D2800FE700A /* Status */; }; @@ -107,7 +107,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 */; platformFilters = (ios, maccatalyst, ); settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; FA31A9AB2A66BF7C00D5F662 /* EditTagGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA31A9AA2A66BF7C00D5F662 /* EditTagGroupView.swift */; }; /* End PBXBuildFile section */ @@ -862,11 +862,19 @@ }; 9FAD859129743F7400496AB1 /* PBXTargetDependency */ = { isa = PBXTargetDependency; + platformFilters = ( + ios, + maccatalyst, + ); target = 9FAD858729743F7400496AB1 /* IceCubesShareExtension */; targetProxy = 9FAD859029743F7400496AB1 /* PBXContainerItemProxy */; }; E9DF420629830FEC0003AAD2 /* PBXTargetDependency */ = { isa = PBXTargetDependency; + platformFilters = ( + ios, + maccatalyst, + ); target = E9DF41F929830FEC0003AAD2 /* IceCubesActionExtension */; targetProxy = E9DF420529830FEC0003AAD2 /* PBXContainerItemProxy */; }; @@ -920,12 +928,13 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,7"; }; name = Debug; }; @@ -954,12 +963,13 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,7"; VALIDATE_PRODUCT = YES; }; name = Release; @@ -1202,13 +1212,13 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp"; PRODUCT_NAME = "Ice Cubes"; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,7"; }; name = Debug; }; @@ -1256,13 +1266,13 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp"; PRODUCT_NAME = "Ice Cubes"; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,7"; }; name = Release; }; diff --git a/IceCubesApp/App/Report/ReportView.swift b/IceCubesApp/App/Report/ReportView.swift index 92d470b3..db7bab81 100644 --- a/IceCubesApp/App/Report/ReportView.swift +++ b/IceCubesApp/App/Report/ReportView.swift @@ -35,9 +35,11 @@ public struct ReportView: View { } .navigationTitle("report.title") .navigationBarTitleDisplayMode(.inline) + #if !os(visionOS) .scrollContentBackground(.hidden) .background(theme.secondaryBackgroundColor) .scrollDismissesKeyboard(.immediately) + #endif .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button { diff --git a/IceCubesApp/App/SafariRouter.swift b/IceCubesApp/App/SafariRouter.swift index e487e5d2..7d41aafc 100644 --- a/IceCubesApp/App/SafariRouter.swift +++ b/IceCubesApp/App/SafariRouter.swift @@ -17,7 +17,9 @@ private struct SafariRouter: ViewModifier { @Environment(UserPreferences.self) private var preferences @Environment(RouterPath.self) private var routerPath + #if !os(visionOS) @State private var safariManager = InAppSafariManager() + #endif func body(content: Content) -> some View { content @@ -50,17 +52,24 @@ private struct SafariRouter: ViewModifier { guard let scheme = url.scheme, ["https", "http"].contains(scheme.lowercased()) else { return .systemAction } + #if os(visionOS) + return .systemAction + #else return safariManager.open(url) + #endif } } + #if !os(visionOS) .background { WindowReader { window in safariManager.windowScene = window.windowScene } } + #endif } } +#if !os(visionOS) @MainActor @Observable private class InAppSafariManager: NSObject, SFSafariViewControllerDelegate { var windowScene: UIWindowScene? @@ -113,6 +122,7 @@ private struct SafariRouter: ViewModifier { } } } +#endif private struct WindowReader: UIViewRepresentable { var onUpdate: (UIWindow) -> Void diff --git a/IceCubesApp/App/Tabs/Settings/AboutView.swift b/IceCubesApp/App/Tabs/Settings/AboutView.swift index 8fb17082..b13c68f9 100644 --- a/IceCubesApp/App/Tabs/Settings/AboutView.swift +++ b/IceCubesApp/App/Tabs/Settings/AboutView.swift @@ -100,8 +100,10 @@ struct AboutView: View { await fetchAccounts() } .listStyle(.insetGrouped) + #if !os(visionOS) .scrollContentBackground(.hidden) .background(theme.secondaryBackgroundColor) + #endif .navigationTitle(Text("settings.about.title")) .navigationBarTitleDisplayMode(.large) .environment(\.openURL, OpenURLAction { url in diff --git a/IceCubesApp/App/Tabs/Settings/AccountSettingView.swift b/IceCubesApp/App/Tabs/Settings/AccountSettingView.swift index bae5c102..d8e3a5b5 100644 --- a/IceCubesApp/App/Tabs/Settings/AccountSettingView.swift +++ b/IceCubesApp/App/Tabs/Settings/AccountSettingView.swift @@ -115,7 +115,9 @@ struct AccountSettingsView: View { cachedPostsCount = await timelineCache.cachedPostsCount(for: appAccountsManager.currentClient.id) } .navigationTitle(account.safeDisplayName) + #if !os(visionOS) .scrollContentBackground(.hidden) .background(theme.secondaryBackgroundColor) + #endif } } diff --git a/IceCubesApp/App/Tabs/Settings/AddAccountsView.swift b/IceCubesApp/App/Tabs/Settings/AddAccountsView.swift index 944a284e..178b0682 100644 --- a/IceCubesApp/App/Tabs/Settings/AddAccountsView.swift +++ b/IceCubesApp/App/Tabs/Settings/AddAccountsView.swift @@ -77,9 +77,11 @@ struct AddAccountView: View { .formStyle(.grouped) .navigationTitle("account.add.navigation-title") .navigationBarTitleDisplayMode(.inline) + #if !os(visionOS) .scrollContentBackground(.hidden) .background(theme.secondaryBackgroundColor) .scrollDismissesKeyboard(.immediately) + #endif .toolbar { if !appAccountsManager.availableAccounts.isEmpty { ToolbarItem(placement: .navigationBarLeading) { diff --git a/IceCubesApp/App/Tabs/Settings/ContentSettingsView.swift b/IceCubesApp/App/Tabs/Settings/ContentSettingsView.swift index fe8a5749..37cba951 100644 --- a/IceCubesApp/App/Tabs/Settings/ContentSettingsView.swift +++ b/IceCubesApp/App/Tabs/Settings/ContentSettingsView.swift @@ -108,7 +108,9 @@ struct ContentSettingsView: View { .listRowBackground(theme.primaryBackgroundColor) } .navigationTitle("settings.content.navigation-title") + #if !os(visionOS) .scrollContentBackground(.hidden) .background(theme.secondaryBackgroundColor) + #endif } } diff --git a/IceCubesApp/App/Tabs/Tabs.swift b/IceCubesApp/App/Tabs/Tabs.swift index 4a9022b8..5114fd0d 100644 --- a/IceCubesApp/App/Tabs/Tabs.swift +++ b/IceCubesApp/App/Tabs/Tabs.swift @@ -20,7 +20,9 @@ enum Tab: Int, Identifiable, Hashable { } static func loggedInTabs() -> [Tab] { - if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { + if UIDevice.current.userInterfaceIdiom == .pad || + UIDevice.current.userInterfaceIdiom == .mac || + UIDevice.current.userInterfaceIdiom == .vision { [.timeline, .trending, .federated, .local, .notifications, .mentions, .explore, .messages, .settings] } else { [.timeline, .notifications, .explore, .messages, .profile] diff --git a/IceCubesApp/App/Tabs/TagGroup/EditTagGroupView.swift b/IceCubesApp/App/Tabs/TagGroup/EditTagGroupView.swift index 2b8edb40..030bdbd1 100644 --- a/IceCubesApp/App/Tabs/TagGroup/EditTagGroupView.swift +++ b/IceCubesApp/App/Tabs/TagGroup/EditTagGroupView.swift @@ -57,9 +57,11 @@ struct EditTagGroupView: View { : "timeline.filter.edit-tag-groups" ) .navigationBarTitleDisplayMode(.inline) + #if !os(visionOS) .scrollContentBackground(.hidden) .background(theme.secondaryBackgroundColor) .scrollDismissesKeyboard(.immediately) + #endif .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button("action.cancel", action: { dismiss() }) diff --git a/IceCubesApp/App/Tabs/Timeline/AddRemoteTimelineView.swift b/IceCubesApp/App/Tabs/Timeline/AddRemoteTimelineView.swift index 9f640516..bc4bc23a 100644 --- a/IceCubesApp/App/Tabs/Timeline/AddRemoteTimelineView.swift +++ b/IceCubesApp/App/Tabs/Timeline/AddRemoteTimelineView.swift @@ -52,9 +52,11 @@ struct AddRemoteTimelineView: View { .formStyle(.grouped) .navigationTitle("timeline.add-remote.title") .navigationBarTitleDisplayMode(.inline) + #if !os(visionOS) .scrollContentBackground(.hidden) .background(theme.secondaryBackgroundColor) .scrollDismissesKeyboard(.immediately) + #endif .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button("action.cancel", action: { dismiss() }) diff --git a/Packages/Account/Package.swift b/Packages/Account/Package.swift index 0df43eb6..fe1b989b 100644 --- a/Packages/Account/Package.swift +++ b/Packages/Account/Package.swift @@ -8,6 +8,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .iOS(.v17), + .visionOS(.v1), ], products: [ .library( diff --git a/Packages/Account/Sources/Account/Edit/EditAccountView.swift b/Packages/Account/Sources/Account/Edit/EditAccountView.swift index 1f0c48c5..ee91b3b7 100644 --- a/Packages/Account/Sources/Account/Edit/EditAccountView.swift +++ b/Packages/Account/Sources/Account/Edit/EditAccountView.swift @@ -30,7 +30,9 @@ public struct EditAccountView: View { .environment(\.editMode, .constant(.active)) .scrollContentBackground(.hidden) .background(theme.secondaryBackgroundColor) + #if !os(visionOS) .scrollDismissesKeyboard(.immediately) + #endif .navigationTitle("account.edit.navigation-title") .navigationBarTitleDisplayMode(.inline) .toolbar { diff --git a/Packages/Account/Sources/Account/Filters/EditFilterView.swift b/Packages/Account/Sources/Account/Filters/EditFilterView.swift index 889b3805..6065eefd 100644 --- a/Packages/Account/Sources/Account/Filters/EditFilterView.swift +++ b/Packages/Account/Sources/Account/Filters/EditFilterView.swift @@ -71,7 +71,9 @@ struct EditFilterView: View { .navigationTitle(filter?.title ?? NSLocalizedString("filter.new", comment: "")) .navigationBarTitleDisplayMode(.inline) .scrollContentBackground(.hidden) + #if !os(visionOS) .scrollDismissesKeyboard(.interactively) + #endif .background(theme.secondaryBackgroundColor) .onAppear { if filter == nil { diff --git a/Packages/AppAccount/Package.swift b/Packages/AppAccount/Package.swift index a2e4e44d..9bc861b6 100644 --- a/Packages/AppAccount/Package.swift +++ b/Packages/AppAccount/Package.swift @@ -8,6 +8,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .iOS(.v17), + .visionOS(.v1), ], products: [ .library( diff --git a/Packages/Conversations/Package.swift b/Packages/Conversations/Package.swift index e113bb99..dce06f6d 100644 --- a/Packages/Conversations/Package.swift +++ b/Packages/Conversations/Package.swift @@ -8,6 +8,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .iOS(.v17), + .visionOS(.v1), ], products: [ .library( diff --git a/Packages/Conversations/Sources/Conversations/Detail/ConversationDetailView.swift b/Packages/Conversations/Sources/Conversations/Detail/ConversationDetailView.swift index fc25bdd8..d424fc51 100644 --- a/Packages/Conversations/Sources/Conversations/Detail/ConversationDetailView.swift +++ b/Packages/Conversations/Sources/Conversations/Detail/ConversationDetailView.swift @@ -46,7 +46,9 @@ public struct ConversationDetailView: View { } .padding(.horizontal, .layoutPadding) } + #if !os(visionOS) .scrollDismissesKeyboard(.interactively) + #endif .safeAreaInset(edge: .bottom) { inputTextView } diff --git a/Packages/Conversations/Sources/Conversations/List/ConversationsListRow.swift b/Packages/Conversations/Sources/Conversations/List/ConversationsListRow.swift index ac54dd8a..aaec8dea 100644 --- a/Packages/Conversations/Sources/Conversations/List/ConversationsListRow.swift +++ b/Packages/Conversations/Sources/Conversations/List/ConversationsListRow.swift @@ -1,4 +1,3 @@ -import Accounts import DesignSystem import Env import Models diff --git a/Packages/DesignSystem/Package.swift b/Packages/DesignSystem/Package.swift index 770538af..bf17f11b 100644 --- a/Packages/DesignSystem/Package.swift +++ b/Packages/DesignSystem/Package.swift @@ -8,6 +8,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .iOS(.v17), + .visionOS(.v1), ], products: [ .library( diff --git a/Packages/DesignSystem/Sources/DesignSystem/SceneDelegate.swift b/Packages/DesignSystem/Sources/DesignSystem/SceneDelegate.swift index 689993ce..67edbd55 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/SceneDelegate.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/SceneDelegate.swift @@ -4,8 +4,13 @@ import UIKit @Observable public class SceneDelegate: NSObject, UIWindowSceneDelegate, Sendable { public var window: UIWindow? + #if os(visionOS) + public private(set) var windowWidth: CGFloat = 0 + public private(set) var windowHeight: CGFloat = 0 + #else public private(set) var windowWidth: CGFloat = UIScreen.main.bounds.size.width public private(set) var windowHeight: CGFloat = UIScreen.main.bounds.size.height + #endif public func scene(_ scene: UIScene, willConnectTo _: UISceneSession, @@ -24,8 +29,13 @@ public class SceneDelegate: NSObject, UIWindowSceneDelegate, Sendable { override public init() { super.init() + #if os(visionOS) + windowWidth = window?.bounds.size.width ?? 0 + windowHeight = window?.bounds.size.height ?? 0 + #else windowWidth = window?.bounds.size.width ?? UIScreen.main.bounds.size.width windowHeight = window?.bounds.size.height ?? UIScreen.main.bounds.size.height + #endif Self.observedSceneDelegate.insert(self) _ = Self.observer // just for activating the lazy static property } @@ -41,15 +51,26 @@ public class SceneDelegate: NSObject, UIWindowSceneDelegate, Sendable { while true { try? await Task.sleep(for: .seconds(0.1)) for delegate in observedSceneDelegate { + #if os(visionOS) + let newWidth = delegate.window?.bounds.size.width ?? 0 + if delegate.windowWidth != newWidth { + delegate.windowWidth = newWidth + } + let newHeight = delegate.window?.bounds.size.height ?? 0 + if delegate.windowHeight != newHeight { + delegate.windowHeight = newHeight + } + #else let newWidth = delegate.window?.bounds.size.width ?? UIScreen.main.bounds.size.width if delegate.windowWidth != newWidth { delegate.windowWidth = newWidth } - let newHeight = delegate.window?.bounds.size.height ?? UIScreen.main.bounds.size.height if delegate.windowHeight != newHeight { delegate.windowHeight = newHeight } + #endif + } } } diff --git a/Packages/Env/Package.swift b/Packages/Env/Package.swift index cfa53709..b545f08c 100644 --- a/Packages/Env/Package.swift +++ b/Packages/Env/Package.swift @@ -8,6 +8,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .iOS(.v17), + .visionOS(.v1), ], products: [ .library( diff --git a/Packages/Env/Sources/Env/HapticManager.swift b/Packages/Env/Sources/Env/HapticManager.swift index 4ffa6e24..bf98c32a 100644 --- a/Packages/Env/Sources/Env/HapticManager.swift +++ b/Packages/Env/Sources/Env/HapticManager.swift @@ -5,27 +5,42 @@ import UIKit public class HapticManager { public static let shared: HapticManager = .init() + #if os(visionOS) + public enum FeedbackType: Int { + case success, warning, error + } + #endif + public enum HapticType { case buttonPress case dataRefresh(intensity: CGFloat) + #if os(visionOS) + case notification(_ type: FeedbackType) + #else case notification(_ type: UINotificationFeedbackGenerator.FeedbackType) + #endif case tabSelection case timeline } + #if !os(visionOS) private let selectionGenerator = UISelectionFeedbackGenerator() private let impactGenerator = UIImpactFeedbackGenerator(style: .heavy) private let notificationGenerator = UINotificationFeedbackGenerator() + #endif private let userPreferences = UserPreferences.shared private init() { + #if !os(visionOS) selectionGenerator.prepare() impactGenerator.prepare() + #endif } @MainActor public func fireHaptic(_ type: HapticType) { + #if !os(visionOS) guard supportsHaptics else { return } switch type { @@ -50,6 +65,7 @@ public class HapticManager { selectionGenerator.selectionChanged() } } + #endif } public var supportsHaptics: Bool { diff --git a/Packages/Explore/Package.swift b/Packages/Explore/Package.swift index 999db826..42af2919 100644 --- a/Packages/Explore/Package.swift +++ b/Packages/Explore/Package.swift @@ -8,6 +8,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .iOS(.v17), + .visionOS(.v1), ], products: [ .library( diff --git a/Packages/Explore/Sources/Explore/ExploreView.swift b/Packages/Explore/Sources/Explore/ExploreView.swift index 675bfd2f..e1bc2de2 100644 --- a/Packages/Explore/Sources/Explore/ExploreView.swift +++ b/Packages/Explore/Sources/Explore/ExploreView.swift @@ -47,7 +47,9 @@ public struct ExploreView: View { ProgressView() Spacer() } + #if !os(visionOS) .listRowBackground(theme.secondaryBackgroundColor) + #endif .listRowSeparator(.hidden) .id(UUID()) } @@ -55,7 +57,9 @@ public struct ExploreView: View { EmptyView(iconName: "magnifyingglass", title: "explore.search.title", message: "explore.search.message-\(client.server)") + #if !os(visionOS) .listRowBackground(theme.secondaryBackgroundColor) + #endif .listRowSeparator(.hidden) } else { quickAccessView @@ -90,8 +94,10 @@ public struct ExploreView: View { } } .listStyle(.plain) + #if !os(visionOS) .scrollContentBackground(.hidden) .background(theme.secondaryBackgroundColor) + #endif .navigationTitle("explore.navigation-title") .searchable(text: $viewModel.searchQuery, isPresented: $viewModel.isSearchPresented, @@ -140,7 +146,9 @@ public struct ExploreView: View { } .scrollIndicators(.never) .listRowInsets(EdgeInsets()) + #if !os(visionOS) .listRowBackground(theme.secondaryBackgroundColor) + #endif .listRowSeparator(.hidden) } @@ -150,7 +158,9 @@ public struct ExploreView: View { .padding(.vertical, 8) .redacted(reason: .placeholder) .allowsHitTesting(false) + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif } } @@ -161,7 +171,9 @@ public struct ExploreView: View { ForEach(results.accounts) { account in if let relationship = results.relationships.first(where: { $0.id == account.id }) { AccountsListRow(viewModel: .init(account: account, relationShip: relationship)) + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif } } } @@ -170,7 +182,9 @@ public struct ExploreView: View { Section("explore.section.tags") { ForEach(results.hashtags) { tag in TagRowView(tag: tag) + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif .padding(.vertical, 4) } } @@ -179,7 +193,9 @@ public struct ExploreView: View { Section("explore.section.posts") { ForEach(results.statuses) { status in StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath)) + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif .padding(.vertical, 8) } } @@ -193,14 +209,18 @@ public struct ExploreView: View { { account in if let relationship = viewModel.suggestedAccountsRelationShips.first(where: { $0.id == account.id }) { AccountsListRow(viewModel: .init(account: account, relationShip: relationship)) + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif } } NavigationLink(value: RouterDestination.accountsList(accounts: viewModel.suggestedAccounts)) { Text("see-more") .foregroundColor(theme.tintColor) } + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif } } @@ -210,14 +230,18 @@ public struct ExploreView: View { .prefix(upTo: viewModel.trendingTags.count > 5 ? 5 : viewModel.trendingTags.count)) { tag in TagRowView(tag: tag) + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif .padding(.vertical, 4) } NavigationLink(value: RouterDestination.tagsList(tags: viewModel.trendingTags)) { Text("see-more") .foregroundColor(theme.tintColor) } + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif } } @@ -227,7 +251,9 @@ public struct ExploreView: View { .prefix(upTo: viewModel.trendingStatuses.count > 3 ? 3 : viewModel.trendingStatuses.count)) { status in StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath)) + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif .padding(.vertical, 8) } @@ -235,7 +261,9 @@ public struct ExploreView: View { Text("see-more") .foregroundColor(theme.tintColor) } + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif } } @@ -245,7 +273,9 @@ public struct ExploreView: View { .prefix(upTo: viewModel.trendingLinks.count > 3 ? 3 : viewModel.trendingLinks.count)) { card in StatusRowCardView(card: card) + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif .padding(.vertical, 8) } @@ -253,7 +283,9 @@ public struct ExploreView: View { Text("see-more") .foregroundColor(theme.tintColor) } + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif } } diff --git a/Packages/Lists/Package.swift b/Packages/Lists/Package.swift index 828c0ecc..403f7920 100644 --- a/Packages/Lists/Package.swift +++ b/Packages/Lists/Package.swift @@ -8,6 +8,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .iOS(.v17), + .visionOS(.v1), ], products: [ .library( diff --git a/Packages/Lists/Sources/Lists/Edit/ListEditView.swift b/Packages/Lists/Sources/Lists/Edit/ListEditView.swift index 0d9acd6c..f45323ce 100644 --- a/Packages/Lists/Sources/Lists/Edit/ListEditView.swift +++ b/Packages/Lists/Sources/Lists/Edit/ListEditView.swift @@ -65,7 +65,9 @@ public struct ListEditView: View { .listRowBackground(theme.primaryBackgroundColor) .disabled(viewModel.isUpdating) } + #if !os(visionOS) .scrollDismissesKeyboard(.immediately) + #endif .scrollContentBackground(.hidden) .background(theme.secondaryBackgroundColor) .toolbar { diff --git a/Packages/MediaUI/Package.swift b/Packages/MediaUI/Package.swift index 73cb24ba..ba6c1e04 100644 --- a/Packages/MediaUI/Package.swift +++ b/Packages/MediaUI/Package.swift @@ -8,6 +8,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .iOS(.v17), + .visionOS(.v1), ], products: [ .library( diff --git a/Packages/Models/Package.swift b/Packages/Models/Package.swift index 3de3587e..d8edd7e4 100644 --- a/Packages/Models/Package.swift +++ b/Packages/Models/Package.swift @@ -8,6 +8,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .iOS(.v17), + .visionOS(.v1), ], products: [ .library( diff --git a/Packages/Network/Package.swift b/Packages/Network/Package.swift index b4702e64..0c8e01fe 100644 --- a/Packages/Network/Package.swift +++ b/Packages/Network/Package.swift @@ -8,6 +8,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .iOS(.v17), + .visionOS(.v1), ], products: [ .library( diff --git a/Packages/Notifications/Package.swift b/Packages/Notifications/Package.swift index 03dae33f..116afea1 100644 --- a/Packages/Notifications/Package.swift +++ b/Packages/Notifications/Package.swift @@ -8,6 +8,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .iOS(.v17), + .visionOS(.v1), ], products: [ .library( diff --git a/Packages/Notifications/Sources/Notifications/NotificationsListView.swift b/Packages/Notifications/Sources/Notifications/NotificationsListView.swift index fe4f8cca..17c86c39 100644 --- a/Packages/Notifications/Sources/Notifications/NotificationsListView.swift +++ b/Packages/Notifications/Sources/Notifications/NotificationsListView.swift @@ -83,8 +83,10 @@ public struct NotificationsListView: View { } } .navigationBarTitleDisplayMode(.inline) + #if !os(visionOS) .scrollContentBackground(.hidden) .background(theme.primaryBackgroundColor) + #endif .task { viewModel.client = client viewModel.currentAccount = account @@ -130,7 +132,9 @@ public struct NotificationsListView: View { leading: .layoutPadding + 4, bottom: 12, trailing: .layoutPadding)) + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif .redacted(reason: .placeholder) .allowsHitTesting(false) } @@ -140,7 +144,9 @@ public struct NotificationsListView: View { EmptyView(iconName: "bell.slash", title: "notifications.empty.title", message: "notifications.empty.message") + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif .listSectionSeparator(.hidden) } else { ForEach(notifications) { notification in @@ -152,8 +158,10 @@ public struct NotificationsListView: View { leading: .layoutPadding + 4, bottom: 12, trailing: .layoutPadding)) + #if !os(visionOS) .listRowBackground(notification.type == .mention && lockedType != .mention ? theme.secondaryBackgroundColor : theme.primaryBackgroundColor) + #endif .id(notification.id) } } @@ -181,7 +189,9 @@ public struct NotificationsListView: View { await viewModel.fetchNotifications() } } + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif .listSectionSeparator(.hidden) } } @@ -196,7 +206,9 @@ public struct NotificationsListView: View { leading: .layoutPadding + 4, bottom: .layoutPadding, trailing: .layoutPadding)) + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) + #endif } private var topPaddingView: some View { diff --git a/Packages/Status/Package.swift b/Packages/Status/Package.swift index fa572d43..8d8fe915 100644 --- a/Packages/Status/Package.swift +++ b/Packages/Status/Package.swift @@ -8,6 +8,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .iOS(.v17), + .visionOS(.v1), ], products: [ .library( diff --git a/Packages/Status/Sources/Status/Editor/Components/GIF/GIFPickerView.swift b/Packages/Status/Sources/Status/Editor/Components/GIF/GIFPickerView.swift index 5fb562d6..14b00b76 100644 --- a/Packages/Status/Sources/Status/Editor/Components/GIF/GIFPickerView.swift +++ b/Packages/Status/Sources/Status/Editor/Components/GIF/GIFPickerView.swift @@ -1,3 +1,4 @@ +#if !os(visionOS) import DesignSystem import GiphyUISDK import SwiftUI @@ -49,3 +50,4 @@ struct GifPickerView: UIViewControllerRepresentable { } } } +#endif diff --git a/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift b/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift index 0a7cae1b..8369032b 100644 --- a/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift +++ b/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift @@ -1,6 +1,8 @@ import DesignSystem import Env +#if !os(visionOS) import GiphyUISDK +#endif import Models import NukeUI import PhotosUI @@ -54,11 +56,13 @@ struct StatusEditorAccessoryView: View { Label("status.editor.browse-file", systemImage: "folder") } + #if !os(visionOS) Button { isGIFPickerPresented = true } label: { Label("GIPHY", systemImage: "party.popper") } + #endif } label: { if viewModel.isMediasLoading { ProgressView() @@ -90,6 +94,7 @@ struct StatusEditorAccessoryView: View { .background(.black) }) .sheet(isPresented: $isGIFPickerPresented, content: { + #if !os(visionOS) GifPickerView { url in GPHCache.shared.downloadAssetData(url) { data, _ in guard let data else { return } @@ -100,6 +105,9 @@ struct StatusEditorAccessoryView: View { isGIFPickerPresented = false } .presentationDetents([.medium, .large]) + #else + EmptyView() + #endif }) .accessibilityLabel("accessibility.editor.button.attach-photo") .disabled(viewModel.showPoll) diff --git a/Packages/Status/Sources/Status/Editor/Components/StatusEditorCameraPickerView.swift b/Packages/Status/Sources/Status/Editor/Components/StatusEditorCameraPickerView.swift index 80e511f0..b3ca568f 100644 --- a/Packages/Status/Sources/Status/Editor/Components/StatusEditorCameraPickerView.swift +++ b/Packages/Status/Sources/Status/Editor/Components/StatusEditorCameraPickerView.swift @@ -21,7 +21,9 @@ struct StatusEditorCameraPickerView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIImagePickerController { let imagePicker = UIImagePickerController() + #if !os(visionOS) imagePicker.sourceType = .camera + #endif imagePicker.delegate = context.coordinator return imagePicker } diff --git a/Packages/Status/Sources/Status/Editor/StatusEditorCoreView.swift b/Packages/Status/Sources/Status/Editor/StatusEditorCoreView.swift index 716694aa..291bea54 100644 --- a/Packages/Status/Sources/Status/Editor/StatusEditorCoreView.swift +++ b/Packages/Status/Sources/Status/Editor/StatusEditorCoreView.swift @@ -1,4 +1,3 @@ -import Accounts import AppAccount import DesignSystem import Env diff --git a/Packages/Status/Sources/Status/Editor/StatusEditorView.swift b/Packages/Status/Sources/Status/Editor/StatusEditorView.swift index ed484a43..bb16a650 100644 --- a/Packages/Status/Sources/Status/Editor/StatusEditorView.swift +++ b/Packages/Status/Sources/Status/Editor/StatusEditorView.swift @@ -1,4 +1,3 @@ -import Accounts import AppAccount import DesignSystem import EmojiText diff --git a/Packages/Status/Sources/Status/Row/StatusRowView.swift b/Packages/Status/Sources/Status/Row/StatusRowView.swift index d7329628..c454d931 100644 --- a/Packages/Status/Sources/Status/Row/StatusRowView.swift +++ b/Packages/Status/Sources/Status/Row/StatusRowView.swift @@ -149,7 +149,9 @@ public struct StatusRowView: View { StatusRowSwipeView(viewModel: viewModel, mode: .leading) } } + #if !os(visionOS) .listRowBackground(viewModel.highlightRowColor) + #endif .listRowInsets(.init(top: 12, leading: .layoutPadding, bottom: 12, diff --git a/Packages/Status/Sources/Status/Row/Subviews/StatusRowMediaPreviewView.swift b/Packages/Status/Sources/Status/Row/Subviews/StatusRowMediaPreviewView.swift index 3426e9ef..b1406f2f 100644 --- a/Packages/Status/Sources/Status/Row/Subviews/StatusRowMediaPreviewView.swift +++ b/Packages/Status/Sources/Status/Row/Subviews/StatusRowMediaPreviewView.swift @@ -22,11 +22,13 @@ public struct StatusRowMediaPreviewView: View { @State private var isQuickLookLoading: Bool = false var availableWidth: CGFloat { + #if !os(visionOS) if UIDevice.current.userInterfaceIdiom == .phone && (UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight) || theme.statusDisplayStyle == .medium { return sceneDelegate.windowWidth * 0.80 } + #endif return sceneDelegate.windowWidth } diff --git a/Packages/Timeline/Package.swift b/Packages/Timeline/Package.swift index 5b6c904f..0d161b53 100644 --- a/Packages/Timeline/Package.swift +++ b/Packages/Timeline/Package.swift @@ -8,6 +8,7 @@ let package = Package( defaultLocalization: "en", platforms: [ .iOS(.v17), + .visionOS(.v1), ], products: [ .library( diff --git a/Packages/Timeline/Sources/Timeline/TimelineView.swift b/Packages/Timeline/Sources/Timeline/TimelineView.swift index e0f5e493..836678eb 100644 --- a/Packages/Timeline/Sources/Timeline/TimelineView.swift +++ b/Packages/Timeline/Sources/Timeline/TimelineView.swift @@ -60,8 +60,10 @@ public struct TimelineView: View { .id(client.id) .environment(\.defaultMinListRowHeight, 1) .listStyle(.plain) + #if !os(visionOS) .scrollContentBackground(.hidden) .background(theme.primaryBackgroundColor) + #endif .introspect(.list, on: .iOS(.v17)) { (collectionView: UICollectionView) in DispatchQueue.main.async { self.collectionView = collectionView