mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-22 08:20:59 +00:00
VisionOS native support (#1758)
* Initial support * UI Adjustments * WIP icons * More UI
This commit is contained in:
parent
ca13e61b53
commit
5a2478c791
66 changed files with 677 additions and 195 deletions
|
@ -67,7 +67,7 @@
|
||||||
9FAD85832971BF7200496AB1 /* Secret.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9FAD85822971BF7200496AB1 /* Secret.plist */; };
|
9FAD85832971BF7200496AB1 /* Secret.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9FAD85822971BF7200496AB1 /* Secret.plist */; };
|
||||||
9FAD858B29743F7400496AB1 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAD858A29743F7400496AB1 /* ShareViewController.swift */; };
|
9FAD858B29743F7400496AB1 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAD858A29743F7400496AB1 /* ShareViewController.swift */; };
|
||||||
9FAD858E29743F7400496AB1 /* (null) in Resources */ = {isa = PBXBuildFile; };
|
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 */; };
|
9FAD85982974405D00496AB1 /* Status in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD85972974405D00496AB1 /* Status */; };
|
||||||
9FAD859A297440CB00496AB1 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD8599297440CB00496AB1 /* KeychainSwift */; };
|
9FAD859A297440CB00496AB1 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD8599297440CB00496AB1 /* KeychainSwift */; };
|
||||||
9FAD859C2974422700496AB1 /* AppAccount in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD859B2974422700496AB1 /* AppAccount */; };
|
9FAD859C2974422700496AB1 /* AppAccount in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD859B2974422700496AB1 /* AppAccount */; };
|
||||||
|
@ -90,7 +90,7 @@
|
||||||
9FD542E72962D2FF0045321A /* Lists in Frameworks */ = {isa = PBXBuildFile; productRef = 9FD542E62962D2FF0045321A /* Lists */; };
|
9FD542E72962D2FF0045321A /* Lists in Frameworks */ = {isa = PBXBuildFile; productRef = 9FD542E62962D2FF0045321A /* Lists */; };
|
||||||
9FE151A6293C90F900E9683D /* IconSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE151A5293C90F900E9683D /* IconSelectorView.swift */; };
|
9FE151A6293C90F900E9683D /* IconSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE151A5293C90F900E9683D /* IconSelectorView.swift */; };
|
||||||
9FE3DB57296FEFCA00628CB0 /* AppAccount in Frameworks */ = {isa = PBXBuildFile; productRef = 9FE3DB56296FEFCA00628CB0 /* AppAccount */; };
|
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 */; };
|
9FF614492B2EDCEC00F7B0E6 /* GiphyUISDK in Frameworks */ = {isa = PBXBuildFile; productRef = 9FF614482B2EDCEC00F7B0E6 /* GiphyUISDK */; };
|
||||||
9FFF677C299B7B2C00FE700A /* Notifications in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF677B299B7B2C00FE700A /* Notifications */; };
|
9FFF677C299B7B2C00FE700A /* Notifications in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF677B299B7B2C00FE700A /* Notifications */; };
|
||||||
9FFF677E299B7D2800FE700A /* Status in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF677D299B7D2800FE700A /* Status */; };
|
9FFF677E299B7D2800FE700A /* Status in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF677D299B7D2800FE700A /* Status */; };
|
||||||
|
@ -107,7 +107,7 @@
|
||||||
E9DF41FC29830FEC0003AAD2 /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9DF41FB29830FEC0003AAD2 /* UniformTypeIdentifiers.framework */; };
|
E9DF41FC29830FEC0003AAD2 /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9DF41FB29830FEC0003AAD2 /* UniformTypeIdentifiers.framework */; };
|
||||||
E9DF420129830FEC0003AAD2 /* ActionRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DF420029830FEC0003AAD2 /* ActionRequestHandler.swift */; };
|
E9DF420129830FEC0003AAD2 /* ActionRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DF420029830FEC0003AAD2 /* ActionRequestHandler.swift */; };
|
||||||
E9DF420329830FEC0003AAD2 /* Action.js in Resources */ = {isa = PBXBuildFile; fileRef = E9DF420229830FEC0003AAD2 /* Action.js */; };
|
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 */; };
|
FA31A9AB2A66BF7C00D5F662 /* EditTagGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA31A9AA2A66BF7C00D5F662 /* EditTagGroupView.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
@ -862,11 +862,19 @@
|
||||||
};
|
};
|
||||||
9FAD859129743F7400496AB1 /* PBXTargetDependency */ = {
|
9FAD859129743F7400496AB1 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
|
platformFilters = (
|
||||||
|
ios,
|
||||||
|
maccatalyst,
|
||||||
|
);
|
||||||
target = 9FAD858729743F7400496AB1 /* IceCubesShareExtension */;
|
target = 9FAD858729743F7400496AB1 /* IceCubesShareExtension */;
|
||||||
targetProxy = 9FAD859029743F7400496AB1 /* PBXContainerItemProxy */;
|
targetProxy = 9FAD859029743F7400496AB1 /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
E9DF420629830FEC0003AAD2 /* PBXTargetDependency */ = {
|
E9DF420629830FEC0003AAD2 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
|
platformFilters = (
|
||||||
|
ios,
|
||||||
|
maccatalyst,
|
||||||
|
);
|
||||||
target = E9DF41F929830FEC0003AAD2 /* IceCubesActionExtension */;
|
target = E9DF41F929830FEC0003AAD2 /* IceCubesActionExtension */;
|
||||||
targetProxy = E9DF420529830FEC0003AAD2 /* PBXContainerItemProxy */;
|
targetProxy = E9DF420529830FEC0003AAD2 /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
|
@ -920,12 +928,13 @@
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator";
|
||||||
SUPPORTS_MACCATALYST = YES;
|
SUPPORTS_MACCATALYST = YES;
|
||||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
|
@ -954,12 +963,13 @@
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator";
|
||||||
SUPPORTS_MACCATALYST = YES;
|
SUPPORTS_MACCATALYST = YES;
|
||||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
@ -1202,13 +1212,13 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp";
|
||||||
PRODUCT_NAME = "Ice Cubes";
|
PRODUCT_NAME = "Ice Cubes";
|
||||||
SDKROOT = auto;
|
SDKROOT = auto;
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator";
|
||||||
SUPPORTS_MACCATALYST = YES;
|
SUPPORTS_MACCATALYST = YES;
|
||||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_STRICT_CONCURRENCY = complete;
|
SWIFT_STRICT_CONCURRENCY = complete;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
|
@ -1256,13 +1266,13 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp";
|
||||||
PRODUCT_NAME = "Ice Cubes";
|
PRODUCT_NAME = "Ice Cubes";
|
||||||
SDKROOT = auto;
|
SDKROOT = auto;
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator";
|
||||||
SUPPORTS_MACCATALYST = YES;
|
SUPPORTS_MACCATALYST = YES;
|
||||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_STRICT_CONCURRENCY = complete;
|
SWIFT_STRICT_CONCURRENCY = complete;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
|
|
@ -97,7 +97,7 @@ extension View {
|
||||||
.preferredColorScheme(Theme.shared.selectedScheme == .dark ? .dark : .light)
|
.preferredColorScheme(Theme.shared.selectedScheme == .dark ? .dark : .light)
|
||||||
case .accountPushNotficationsSettings:
|
case .accountPushNotficationsSettings:
|
||||||
if let subscription = PushNotificationsService.shared.subscriptions.first(where: { $0.account.token == AppAccountsManager.shared.currentAccount.oauthToken }) {
|
if let subscription = PushNotificationsService.shared.subscriptions.first(where: { $0.account.token == AppAccountsManager.shared.currentAccount.oauthToken }) {
|
||||||
PushNotificationsView(subscription: subscription)
|
PushNotificationsViewWrapper(subscription: subscription)
|
||||||
} else {
|
} else {
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,9 +35,11 @@ public struct ReportView: View {
|
||||||
}
|
}
|
||||||
.navigationTitle("report.title")
|
.navigationTitle("report.title")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
.scrollDismissesKeyboard(.immediately)
|
.scrollDismissesKeyboard(.immediately)
|
||||||
|
#endif
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
Button {
|
Button {
|
||||||
|
|
|
@ -17,7 +17,9 @@ private struct SafariRouter: ViewModifier {
|
||||||
@Environment(UserPreferences.self) private var preferences
|
@Environment(UserPreferences.self) private var preferences
|
||||||
@Environment(RouterPath.self) private var routerPath
|
@Environment(RouterPath.self) private var routerPath
|
||||||
|
|
||||||
|
#if !os(visionOS)
|
||||||
@State private var safariManager = InAppSafariManager()
|
@State private var safariManager = InAppSafariManager()
|
||||||
|
#endif
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
content
|
content
|
||||||
|
@ -50,17 +52,24 @@ private struct SafariRouter: ViewModifier {
|
||||||
guard let scheme = url.scheme, ["https", "http"].contains(scheme.lowercased()) else {
|
guard let scheme = url.scheme, ["https", "http"].contains(scheme.lowercased()) else {
|
||||||
return .systemAction
|
return .systemAction
|
||||||
}
|
}
|
||||||
|
#if os(visionOS)
|
||||||
|
return .systemAction
|
||||||
|
#else
|
||||||
return safariManager.open(url)
|
return safariManager.open(url)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.background {
|
.background {
|
||||||
WindowReader { window in
|
WindowReader { window in
|
||||||
safariManager.windowScene = window.windowScene
|
safariManager.windowScene = window.windowScene
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !os(visionOS)
|
||||||
@MainActor
|
@MainActor
|
||||||
@Observable private class InAppSafariManager: NSObject, SFSafariViewControllerDelegate {
|
@Observable private class InAppSafariManager: NSObject, SFSafariViewControllerDelegate {
|
||||||
var windowScene: UIWindowScene?
|
var windowScene: UIWindowScene?
|
||||||
|
@ -113,6 +122,7 @@ private struct SafariRouter: ViewModifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
private struct WindowReader: UIViewRepresentable {
|
private struct WindowReader: UIViewRepresentable {
|
||||||
var onUpdate: (UIWindow) -> Void
|
var onUpdate: (UIWindow) -> Void
|
||||||
|
|
|
@ -57,7 +57,9 @@ struct AboutView: View {
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("\(versionNumber)©2023 Thomas Ricouard")
|
Text("\(versionNumber)©2023 Thomas Ricouard")
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
|
|
||||||
followAccountsSection
|
followAccountsSection
|
||||||
|
|
||||||
|
@ -94,14 +96,18 @@ struct AboutView: View {
|
||||||
Text("settings.about.built-with")
|
Text("settings.about.built-with")
|
||||||
.textCase(nil)
|
.textCase(nil)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
await fetchAccounts()
|
await fetchAccounts()
|
||||||
}
|
}
|
||||||
.listStyle(.insetGrouped)
|
.listStyle(.insetGrouped)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.navigationTitle(Text("settings.about.title"))
|
.navigationTitle(Text("settings.about.title"))
|
||||||
.navigationBarTitleDisplayMode(.large)
|
.navigationBarTitleDisplayMode(.large)
|
||||||
.environment(\.openURL, OpenURLAction { url in
|
.environment(\.openURL, OpenURLAction { url in
|
||||||
|
@ -116,12 +122,16 @@ struct AboutView: View {
|
||||||
AccountsListRow(viewModel: iceCubesAccount)
|
AccountsListRow(viewModel: iceCubesAccount)
|
||||||
AccountsListRow(viewModel: dimillianAccount)
|
AccountsListRow(viewModel: dimillianAccount)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
Section {
|
Section {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,9 @@ struct AccountSettingsView: View {
|
||||||
cachedPostsCount = await timelineCache.cachedPostsCount(for: appAccountsManager.currentClient.id)
|
cachedPostsCount = await timelineCache.cachedPostsCount(for: appAccountsManager.currentClient.id)
|
||||||
}
|
}
|
||||||
.navigationTitle(account.safeDisplayName)
|
.navigationTitle(account.safeDisplayName)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,9 @@ struct AddAccountView: View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
Form {
|
Form {
|
||||||
TextField("instance.url", text: $instanceName)
|
TextField("instance.url", text: $instanceName)
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.keyboardType(.URL)
|
.keyboardType(.URL)
|
||||||
.textContentType(.URL)
|
.textContentType(.URL)
|
||||||
.textInputAutocapitalization(.never)
|
.textInputAutocapitalization(.never)
|
||||||
|
@ -77,9 +79,11 @@ struct AddAccountView: View {
|
||||||
.formStyle(.grouped)
|
.formStyle(.grouped)
|
||||||
.navigationTitle("account.add.navigation-title")
|
.navigationTitle("account.add.navigation-title")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
.scrollDismissesKeyboard(.immediately)
|
.scrollDismissesKeyboard(.immediately)
|
||||||
|
#endif
|
||||||
.toolbar {
|
.toolbar {
|
||||||
if !appAccountsManager.availableAccounts.isEmpty {
|
if !appAccountsManager.availableAccounts.isEmpty {
|
||||||
ToolbarItem(placement: .navigationBarLeading) {
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
|
@ -164,7 +168,9 @@ struct AddAccountView: View {
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderedProminent)
|
.buttonStyle(.borderedProminent)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.tintColor)
|
.listRowBackground(theme.tintColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var instancesListView: some View {
|
private var instancesListView: some View {
|
||||||
|
@ -210,11 +216,13 @@ struct AddAccountView: View {
|
||||||
.padding(10)
|
.padding(10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.background(theme.primaryBackgroundColor)
|
.background(theme.primaryBackgroundColor)
|
||||||
.listRowBackground(Color.clear)
|
.listRowBackground(Color.clear)
|
||||||
.listRowInsets(EdgeInsets(top: 10, leading: 0, bottom: 10, trailing: 0))
|
.listRowInsets(EdgeInsets(top: 10, leading: 0, bottom: 10, trailing: 0))
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 5))
|
.clipShape(RoundedRectangle(cornerRadius: 5))
|
||||||
|
#endif
|
||||||
.overlay {
|
.overlay {
|
||||||
RoundedRectangle(cornerRadius: 5)
|
RoundedRectangle(cornerRadius: 5)
|
||||||
.stroke(lineWidth: 1)
|
.stroke(lineWidth: 1)
|
||||||
|
@ -249,7 +257,9 @@ struct AddAccountView: View {
|
||||||
.redacted(reason: .placeholder)
|
.redacted(reason: .placeholder)
|
||||||
.allowsHitTesting(false)
|
.allowsHitTesting(false)
|
||||||
.shimmering()
|
.shimmering()
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private func signIn() async {
|
private func signIn() async {
|
||||||
|
|
|
@ -19,7 +19,10 @@ struct ContentSettingsView: View {
|
||||||
Toggle(isOn: $userPreferences.suppressDupeReblogs) {
|
Toggle(isOn: $userPreferences.suppressDupeReblogs) {
|
||||||
Text("settings.content.hide-repeated-boosts")
|
Text("settings.content.hide-repeated-boosts")
|
||||||
}
|
}
|
||||||
}.listRowBackground(theme.primaryBackgroundColor)
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
|
|
||||||
Section("settings.content.media") {
|
Section("settings.content.media") {
|
||||||
Toggle(isOn: $userPreferences.autoPlayVideo) {
|
Toggle(isOn: $userPreferences.autoPlayVideo) {
|
||||||
|
@ -28,7 +31,10 @@ struct ContentSettingsView: View {
|
||||||
Toggle(isOn: $userPreferences.showAltTextForMedia) {
|
Toggle(isOn: $userPreferences.showAltTextForMedia) {
|
||||||
Text("settings.content.media.show.alt")
|
Text("settings.content.media.show.alt")
|
||||||
}
|
}
|
||||||
}.listRowBackground(theme.primaryBackgroundColor)
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
|
|
||||||
Section("settings.content.sharing") {
|
Section("settings.content.sharing") {
|
||||||
Picker("settings.content.sharing.share-button-behavior", selection: $userPreferences.shareButtonBehavior) {
|
Picker("settings.content.sharing.share-button-behavior", selection: $userPreferences.shareButtonBehavior) {
|
||||||
|
@ -38,14 +44,18 @@ struct ContentSettingsView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
|
|
||||||
Section("settings.content.instance-settings") {
|
Section("settings.content.instance-settings") {
|
||||||
Toggle(isOn: $userPreferences.useInstanceContentSettings) {
|
Toggle(isOn: $userPreferences.useInstanceContentSettings) {
|
||||||
Text("settings.content.use-instance-settings")
|
Text("settings.content.use-instance-settings")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.onChange(of: userPreferences.useInstanceContentSettings) { _, newVal in
|
.onChange(of: userPreferences.useInstanceContentSettings) { _, newVal in
|
||||||
if newVal {
|
if newVal {
|
||||||
userPreferences.appAutoExpandSpoilers = userPreferences.autoExpandSpoilers
|
userPreferences.appAutoExpandSpoilers = userPreferences.autoExpandSpoilers
|
||||||
|
@ -76,7 +86,9 @@ struct ContentSettingsView: View {
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("settings.content.collapse-long-posts-hint")
|
Text("settings.content.collapse-long-posts-hint")
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
|
|
||||||
Section("settings.content.posting") {
|
Section("settings.content.posting") {
|
||||||
Picker("settings.content.default-visibility", selection: $userPreferences.appDefaultPostVisibility) {
|
Picker("settings.content.default-visibility", selection: $userPreferences.appDefaultPostVisibility) {
|
||||||
|
@ -104,11 +116,14 @@ struct ContentSettingsView: View {
|
||||||
}
|
}
|
||||||
.disabled(userPreferences.useInstanceContentSettings)
|
.disabled(userPreferences.useInstanceContentSettings)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
.navigationTitle("settings.content.navigation-title")
|
.navigationTitle("settings.content.navigation-title")
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,8 +46,10 @@ struct DisplaySettingsView: View {
|
||||||
resetSection
|
resetSection
|
||||||
}
|
}
|
||||||
.navigationTitle("settings.display.navigation-title")
|
.navigationTitle("settings.display.navigation-title")
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.task(id: localValues.tintColor) {
|
.task(id: localValues.tintColor) {
|
||||||
do { try await Task.sleep(for: .microseconds(500)) } catch {}
|
do { try await Task.sleep(for: .microseconds(500)) } catch {}
|
||||||
theme.tintColor = localValues.tintColor
|
theme.tintColor = localValues.tintColor
|
||||||
|
@ -121,7 +123,9 @@ struct DisplaySettingsView: View {
|
||||||
Text("settings.display.section.theme.footer")
|
Text("settings.display.section.theme.footer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var fontSection: some View {
|
private var fontSection: some View {
|
||||||
|
@ -173,7 +177,9 @@ struct DisplaySettingsView: View {
|
||||||
d[.leading]
|
d[.leading]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
|
@ -222,7 +228,9 @@ struct DisplaySettingsView: View {
|
||||||
}
|
}
|
||||||
Toggle("settings.display.show-account-popover", isOn: $userPreferences.showAccountPopover)
|
Toggle("settings.display.show-account-popover", isOn: $userPreferences.showAccountPopover)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
|
@ -232,14 +240,18 @@ struct DisplaySettingsView: View {
|
||||||
Section("iPhone") {
|
Section("iPhone") {
|
||||||
Toggle("settings.display.show-tab-label", isOn: $userPreferences.showiPhoneTabLabel)
|
Toggle("settings.display.show-tab-label", isOn: $userPreferences.showiPhoneTabLabel)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||||
Section("iPad") {
|
Section("iPad") {
|
||||||
Toggle("settings.display.show-ipad-column", isOn: $userPreferences.showiPadSecondaryColumn)
|
Toggle("settings.display.show-ipad-column", isOn: $userPreferences.showiPadSecondaryColumn)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +273,9 @@ struct DisplaySettingsView: View {
|
||||||
Text("settings.display.restore")
|
Text("settings.display.restore")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var themeSelectorButton: some View {
|
private var themeSelectorButton: some View {
|
||||||
|
|
|
@ -17,10 +17,14 @@ struct HapticSettingsView: View {
|
||||||
Toggle("settings.haptic.tab-selection", isOn: $userPreferences.hapticTabSelectionEnabled)
|
Toggle("settings.haptic.tab-selection", isOn: $userPreferences.hapticTabSelectionEnabled)
|
||||||
Toggle("settings.haptic.buttons", isOn: $userPreferences.hapticButtonPressEnabled)
|
Toggle("settings.haptic.buttons", isOn: $userPreferences.hapticButtonPressEnabled)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
.navigationTitle("settings.haptic.navigation-title")
|
.navigationTitle("settings.haptic.navigation-title")
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,9 @@ struct IconSelectorView: View {
|
||||||
.padding(6)
|
.padding(6)
|
||||||
.navigationTitle("settings.app.icon.navigation-title")
|
.navigationTitle("settings.app.icon.navigation-title")
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.background(theme.primaryBackgroundColor)
|
.background(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makeIconGridView(icons: [Icon]) -> some View {
|
private func makeIconGridView(icons: [Icon]) -> some View {
|
||||||
|
@ -116,6 +118,7 @@ struct IconSelectorView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,10 @@ struct InstanceInfoView: View {
|
||||||
InstanceInfoSection(instance: instance)
|
InstanceInfoSection(instance: instance)
|
||||||
}
|
}
|
||||||
.navigationTitle("instance.info.navigation-title")
|
.navigationTitle("instance.info.navigation-title")
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +37,9 @@ public struct InstanceInfoSection: View {
|
||||||
LabeledContent("instance.info.posts", value: format(instance.stats.statusCount))
|
LabeledContent("instance.info.posts", value: format(instance.stats.statusCount))
|
||||||
LabeledContent("instance.info.domains", value: format(instance.stats.domainCount))
|
LabeledContent("instance.info.domains", value: format(instance.stats.domainCount))
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
|
|
||||||
if let rules = instance.rules {
|
if let rules = instance.rules {
|
||||||
Section("instance.info.section.rules") {
|
Section("instance.info.section.rules") {
|
||||||
|
@ -43,7 +47,9 @@ public struct InstanceInfoSection: View {
|
||||||
Text(rule.text.trimmingCharacters(in: .whitespacesAndNewlines))
|
Text(rule.text.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,27 @@ import NukeUI
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
|
|
||||||
|
struct PushNotificationsViewWrapper: View {
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
|
public let subscription: PushNotificationSubscriptionSettings
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack {
|
||||||
|
PushNotificationsView(subscription: subscription)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
|
Button {
|
||||||
|
dismiss()
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "xmark.circle")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
struct PushNotificationsView: View {
|
struct PushNotificationsView: View {
|
||||||
@Environment(Theme.self) private var theme
|
@Environment(Theme.self) private var theme
|
||||||
|
@ -33,7 +54,9 @@ struct PushNotificationsView: View {
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("settings.push.main-toggle.description")
|
Text("settings.push.main-toggle.description")
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
|
|
||||||
if subscription.isEnabled {
|
if subscription.isEnabled {
|
||||||
Section {
|
Section {
|
||||||
|
@ -86,7 +109,9 @@ struct PushNotificationsView: View {
|
||||||
Label("settings.push.new-posts", systemImage: "bubble.right")
|
Label("settings.push.new-posts", systemImage: "bubble.right")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
|
@ -101,11 +126,15 @@ struct PushNotificationsView: View {
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("settings.push.duplicate.footer")
|
Text("settings.push.duplicate.footer")
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
.navigationTitle("settings.push.navigation-title")
|
.navigationTitle("settings.push.navigation-title")
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.task {
|
.task {
|
||||||
await subscription.fetchSubscription()
|
await subscription.fetchSubscription()
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,9 @@ struct SettingsTabs: View {
|
||||||
cacheSection
|
cacheSection
|
||||||
}
|
}
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
|
#if !os(visionOS)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.navigationTitle(Text("settings.title"))
|
.navigationTitle(Text("settings.title"))
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbarBackground(theme.primaryBackgroundColor.opacity(0.50), for: .navigationBar)
|
.toolbarBackground(theme.primaryBackgroundColor.opacity(0.50), for: .navigationBar)
|
||||||
|
@ -114,7 +116,9 @@ struct SettingsTabs: View {
|
||||||
}
|
}
|
||||||
addAccountButton
|
addAccountButton
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private func logoutAccount(account: AppAccount) async {
|
private func logoutAccount(account: AppAccount) async {
|
||||||
|
@ -166,7 +170,9 @@ struct SettingsTabs: View {
|
||||||
.tint(theme.labelColor)
|
.tint(theme.labelColor)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
|
@ -204,12 +210,14 @@ struct SettingsTabs: View {
|
||||||
Label("settings.other.fast-refresh", systemImage: "arrow.clockwise")
|
Label("settings.other.fast-refresh", systemImage: "arrow.clockwise")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var appSection: some View {
|
private var appSection: some View {
|
||||||
Section {
|
Section {
|
||||||
#if !targetEnvironment(macCatalyst)
|
#if !targetEnvironment(macCatalyst) && !os(visionOS)
|
||||||
NavigationLink(destination: IconSelectorView()) {
|
NavigationLink(destination: IconSelectorView()) {
|
||||||
Label {
|
Label {
|
||||||
Text("settings.app.icon")
|
Text("settings.app.icon")
|
||||||
|
@ -252,7 +260,9 @@ struct SettingsTabs: View {
|
||||||
Text("settings.section.app.footer \(appVersion)").frame(maxWidth: .infinity, alignment: .center)
|
Text("settings.section.app.footer \(appVersion)").frame(maxWidth: .infinity, alignment: .center)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var addAccountButton: some View {
|
private var addAccountButton: some View {
|
||||||
|
@ -293,18 +303,24 @@ struct SettingsTabs: View {
|
||||||
context.delete(tagGroups[index])
|
context.delete(tagGroups[index])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
routerPath.presentedSheet = .addTagGroup
|
routerPath.presentedSheet = .addTagGroup
|
||||||
} label: {
|
} label: {
|
||||||
Label("timeline.filter.add-tag-groups", systemImage: "plus")
|
Label("timeline.filter.add-tag-groups", systemImage: "plus")
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
.navigationTitle("timeline.filter.tag-groups")
|
.navigationTitle("timeline.filter.tag-groups")
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
|
#if !os(visionOS)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.toolbar {
|
.toolbar {
|
||||||
EditButton()
|
EditButton()
|
||||||
}
|
}
|
||||||
|
@ -319,17 +335,23 @@ struct SettingsTabs: View {
|
||||||
context.delete(localTimelines[index])
|
context.delete(localTimelines[index])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
Button {
|
Button {
|
||||||
routerPath.presentedSheet = .addRemoteLocalTimeline
|
routerPath.presentedSheet = .addRemoteLocalTimeline
|
||||||
} label: {
|
} label: {
|
||||||
Label("settings.timeline.add", systemImage: "badge.plus.radiowaves.right")
|
Label("settings.timeline.add", systemImage: "badge.plus.radiowaves.right")
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
.navigationTitle("settings.general.remote-timelines")
|
.navigationTitle("settings.general.remote-timelines")
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
|
#if !os(visionOS)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.toolbar {
|
.toolbar {
|
||||||
EditButton()
|
EditButton()
|
||||||
}
|
}
|
||||||
|
@ -349,6 +371,8 @@ struct SettingsTabs: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,8 +69,10 @@ struct SupportAppView: View {
|
||||||
linksSection
|
linksSection
|
||||||
}
|
}
|
||||||
.navigationTitle("settings.support.navigation-title")
|
.navigationTitle("settings.support.navigation-title")
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.alert("settings.support.alert.title", isPresented: $purchaseSuccessDisplayed, actions: {
|
.alert("settings.support.alert.title", isPresented: $purchaseSuccessDisplayed, actions: {
|
||||||
Button { purchaseSuccessDisplayed = false } label: { Text("alert.button.ok") }
|
Button { purchaseSuccessDisplayed = false } label: { Text("alert.button.ok") }
|
||||||
}, message: {
|
}, message: {
|
||||||
|
@ -151,7 +153,9 @@ struct SupportAppView: View {
|
||||||
Text("settings.support.message-from-dev")
|
Text("settings.support.message-from-dev")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var subscriptionSection: some View {
|
private var subscriptionSection: some View {
|
||||||
|
@ -188,7 +192,9 @@ struct SupportAppView: View {
|
||||||
Text("settings.support.supporter.subscription-info")
|
Text("settings.support.supporter.subscription-info")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var tipsSection: some View {
|
private var tipsSection: some View {
|
||||||
|
@ -213,7 +219,9 @@ struct SupportAppView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var restorePurchase: some View {
|
private var restorePurchase: some View {
|
||||||
|
@ -232,7 +240,9 @@ struct SupportAppView: View {
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("settings.support.restore-purchase.explanation")
|
Text("settings.support.restore-purchase.explanation")
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.secondaryBackgroundColor)
|
.listRowBackground(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var linksSection: some View {
|
private var linksSection: some View {
|
||||||
|
@ -252,7 +262,9 @@ struct SupportAppView: View {
|
||||||
.buttonStyle(.borderless)
|
.buttonStyle(.borderless)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.secondaryBackgroundColor)
|
.listRowBackground(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var loadingPlaceholder: some View {
|
private var loadingPlaceholder: some View {
|
||||||
|
|
|
@ -46,8 +46,10 @@ struct SwipeActionsSettingsView: View {
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("settings.swipeactions.status.explanation")
|
Text("settings.swipeactions.status.explanation")
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Picker(selection: $userPreferences.swipeActionsIconStyle, label: Text("settings.swipeactions.icon-style")) {
|
Picker(selection: $userPreferences.swipeActionsIconStyle, label: Text("settings.swipeactions.icon-style")) {
|
||||||
ForEach(UserPreferences.SwipeActionsIconStyle.allCases, id: \.rawValue) { style in
|
ForEach(UserPreferences.SwipeActionsIconStyle.allCases, id: \.rawValue) { style in
|
||||||
|
@ -62,11 +64,15 @@ struct SwipeActionsSettingsView: View {
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("settings.swipeactions.use-theme-colors-explanation")
|
Text("settings.swipeactions.use-theme-colors-explanation")
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
.navigationTitle("settings.swipeactions.navigation-title")
|
.navigationTitle("settings.swipeactions.navigation-title")
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createStatusActionPicker(selection: Binding<StatusAction>, label: LocalizedStringKey) -> some View {
|
private func createStatusActionPicker(selection: Binding<StatusAction>, label: LocalizedStringKey) -> some View {
|
||||||
|
|
|
@ -21,8 +21,10 @@ struct TranslationSettingsView: View {
|
||||||
.onAppear {
|
.onAppear {
|
||||||
readValue()
|
readValue()
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
|
|
||||||
if apiKey.isEmpty {
|
if apiKey.isEmpty {
|
||||||
Section {
|
Section {
|
||||||
Link(destination: URL(string: "https://www.deepl.com/pro-api")!) {
|
Link(destination: URL(string: "https://www.deepl.com/pro-api")!) {
|
||||||
|
@ -30,14 +32,18 @@ struct TranslationSettingsView: View {
|
||||||
.foregroundColor(.red)
|
.foregroundColor(.red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
autoDetectSection
|
autoDetectSection
|
||||||
}
|
}
|
||||||
.navigationTitle("settings.translation.navigation-title")
|
.navigationTitle("settings.translation.navigation-title")
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.onChange(of: apiKey) {
|
.onChange(of: apiKey) {
|
||||||
writeNewValue()
|
writeNewValue()
|
||||||
}
|
}
|
||||||
|
@ -50,7 +56,9 @@ struct TranslationSettingsView: View {
|
||||||
Toggle(isOn: $preferences.alwaysUseDeepl) {
|
Toggle(isOn: $preferences.alwaysUseDeepl) {
|
||||||
Text("settings.translation.always-deepl")
|
Text("settings.translation.always-deepl")
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
|
|
|
@ -20,8 +20,11 @@ enum Tab: Int, Identifiable, Hashable {
|
||||||
}
|
}
|
||||||
|
|
||||||
static func loggedInTabs() -> [Tab] {
|
static func loggedInTabs() -> [Tab] {
|
||||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
if UIDevice.current.userInterfaceIdiom == .pad ||
|
||||||
|
UIDevice.current.userInterfaceIdiom == .mac {
|
||||||
[.timeline, .trending, .federated, .local, .notifications, .mentions, .explore, .messages, .settings]
|
[.timeline, .trending, .federated, .local, .notifications, .mentions, .explore, .messages, .settings]
|
||||||
|
} else if UIDevice.current.userInterfaceIdiom == .vision {
|
||||||
|
[.profile, .timeline, .trending, .federated, .local, .notifications, .mentions, .explore, .messages, .settings]
|
||||||
} else {
|
} else {
|
||||||
[.timeline, .notifications, .explore, .messages, .profile]
|
[.timeline, .notifications, .explore, .messages, .profile]
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,9 +57,11 @@ struct EditTagGroupView: View {
|
||||||
: "timeline.filter.edit-tag-groups"
|
: "timeline.filter.edit-tag-groups"
|
||||||
)
|
)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
.scrollDismissesKeyboard(.immediately)
|
.scrollDismissesKeyboard(.immediately)
|
||||||
|
#endif
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .navigationBarLeading) {
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
Button("action.cancel", action: { dismiss() })
|
Button("action.cancel", action: { dismiss() })
|
||||||
|
|
|
@ -52,9 +52,11 @@ struct AddRemoteTimelineView: View {
|
||||||
.formStyle(.grouped)
|
.formStyle(.grouped)
|
||||||
.navigationTitle("timeline.add-remote.title")
|
.navigationTitle("timeline.add-remote.title")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
.scrollDismissesKeyboard(.immediately)
|
.scrollDismissesKeyboard(.immediately)
|
||||||
|
#endif
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .navigationBarLeading) {
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
Button("action.cancel", action: { dismiss() })
|
Button("action.cancel", action: { dismiss() })
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 846 KiB |
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "1024.png",
|
||||||
|
"idiom" : "vision",
|
||||||
|
"scale" : "2x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"layers" : [
|
||||||
|
{
|
||||||
|
"filename" : "Front.solidimagestacklayer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Back.solidimagestacklayer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 846 KiB |
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "1024.png",
|
||||||
|
"idiom" : "vision",
|
||||||
|
"scale" : "2x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ let package = Package(
|
||||||
defaultLocalization: "en",
|
defaultLocalization: "en",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v17),
|
.iOS(.v17),
|
||||||
|
.visionOS(.v1),
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -74,7 +74,9 @@ struct AccountDetailHeaderView: View {
|
||||||
.frame(height: Constants.headerHeight)
|
.frame(height: Constants.headerHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.frame(height: Constants.headerHeight)
|
.frame(height: Constants.headerHeight)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
guard account.haveHeader else {
|
guard account.haveHeader else {
|
||||||
|
@ -320,7 +322,9 @@ struct AccountDetailHeaderView: View {
|
||||||
Text(note)
|
Text(note)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
#if !os(visionOS)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.cornerRadius(4)
|
.cornerRadius(4)
|
||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: 4)
|
RoundedRectangle(cornerRadius: 4)
|
||||||
|
@ -370,7 +374,11 @@ struct AccountDetailHeaderView: View {
|
||||||
.padding(8)
|
.padding(8)
|
||||||
.accessibilityElement(children: .contain)
|
.accessibilityElement(children: .contain)
|
||||||
.accessibilityLabel("accessibility.tabs.profile.fields.container.label")
|
.accessibilityLabel("accessibility.tabs.profile.fields.container.label")
|
||||||
|
#if os(visionOS)
|
||||||
|
.background(Material.thick)
|
||||||
|
#else
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.cornerRadius(4)
|
.cornerRadius(4)
|
||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: 4)
|
RoundedRectangle(cornerRadius: 4)
|
||||||
|
|
|
@ -84,8 +84,10 @@ public struct AccountDetailView: View {
|
||||||
}
|
}
|
||||||
.environment(\.defaultMinListRowHeight, 1)
|
.environment(\.defaultMinListRowHeight, 1)
|
||||||
.listStyle(.plain)
|
.listStyle(.plain)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.primaryBackgroundColor)
|
.background(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.onChange(of: scrollToTopSignal) {
|
.onChange(of: scrollToTopSignal) {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
proxy.scrollTo(ScrollToView.Constants.scrollToTop, anchor: .top)
|
proxy.scrollTo(ScrollToView.Constants.scrollToTop, anchor: .top)
|
||||||
|
@ -216,7 +218,9 @@ public struct AccountDetailView: View {
|
||||||
.padding(.leading, -4)
|
.padding(.leading, -4)
|
||||||
.accessibilityLabel(account.safeDisplayName)
|
.accessibilityLabel(account.safeDisplayName)
|
||||||
|
|
||||||
}.accessibilityAddTraits(.isImage)
|
}
|
||||||
|
.accessibilityAddTraits(.isImage)
|
||||||
|
.buttonStyle(.plain)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.leading, .layoutPadding + 4)
|
.padding(.leading, .layoutPadding + 4)
|
||||||
|
@ -235,7 +239,9 @@ public struct AccountDetailView: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
Image(systemName: "chevron.right")
|
Image(systemName: "chevron.right")
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}.task {
|
}.task {
|
||||||
await currentAccount.fetchFollowedTags()
|
await currentAccount.fetchFollowedTags()
|
||||||
|
@ -250,7 +256,9 @@ public struct AccountDetailView: View {
|
||||||
.font(.scaledHeadline)
|
.font(.scaledHeadline)
|
||||||
.foregroundColor(theme.labelColor)
|
.foregroundColor(theme.labelColor)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.contextMenu {
|
.contextMenu {
|
||||||
Button("account.list.delete", role: .destructive) {
|
Button("account.list.delete", role: .destructive) {
|
||||||
Task {
|
Task {
|
||||||
|
@ -264,7 +272,9 @@ public struct AccountDetailView: View {
|
||||||
}
|
}
|
||||||
.tint(theme.tintColor)
|
.tint(theme.tintColor)
|
||||||
.buttonStyle(.borderless)
|
.buttonStyle(.borderless)
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
await currentAccount.fetchLists()
|
await currentAccount.fetchLists()
|
||||||
|
@ -284,7 +294,9 @@ public struct AccountDetailView: View {
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
trailing: .layoutPadding))
|
trailing: .layoutPadding))
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
ForEach(viewModel.pinned) { status in
|
ForEach(viewModel.pinned) { status in
|
||||||
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
|
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
|
||||||
}
|
}
|
||||||
|
@ -403,7 +415,9 @@ extension View {
|
||||||
func applyAccountDetailsRowStyle(theme: Theme) -> some View {
|
func applyAccountDetailsRowStyle(theme: Theme) -> some View {
|
||||||
listRowInsets(.init())
|
listRowInsets(.init())
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,9 @@ public struct AccountsListView: View {
|
||||||
.redacted(reason: .placeholder)
|
.redacted(reason: .placeholder)
|
||||||
.allowsHitTesting(false)
|
.allowsHitTesting(false)
|
||||||
.shimmering()
|
.shimmering()
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
case let .display(accounts, relationships, nextPageState):
|
case let .display(accounts, relationships, nextPageState):
|
||||||
if case .followers = viewModel.mode,
|
if case .followers = viewModel.mode,
|
||||||
|
@ -49,7 +51,9 @@ public struct AccountsListView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +62,9 @@ public struct AccountsListView: View {
|
||||||
if let relationship = relationships.first(where: { $0.id == account.id }) {
|
if let relationship = relationships.first(where: { $0.id == account.id }) {
|
||||||
AccountsListRow(viewModel: .init(account: account,
|
AccountsListRow(viewModel: .init(account: account,
|
||||||
relationShip: relationship))
|
relationShip: relationship))
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +72,9 @@ public struct AccountsListView: View {
|
||||||
switch nextPageState {
|
switch nextPageState {
|
||||||
case .hasNextPage:
|
case .hasNextPage:
|
||||||
loadingRow
|
loadingRow
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.onAppear {
|
.onAppear {
|
||||||
Task {
|
Task {
|
||||||
await viewModel.fetchNextPage()
|
await viewModel.fetchNextPage()
|
||||||
|
@ -75,18 +83,24 @@ public struct AccountsListView: View {
|
||||||
|
|
||||||
case .loadingNextPage:
|
case .loadingNextPage:
|
||||||
loadingRow
|
loadingRow
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
case .none:
|
case .none:
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
case let .error(error):
|
case let .error(error):
|
||||||
Text(error.localizedDescription)
|
Text(error.localizedDescription)
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.primaryBackgroundColor)
|
.background(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.listStyle(.plain)
|
.listStyle(.plain)
|
||||||
.navigationTitle(viewModel.mode.title)
|
.navigationTitle(viewModel.mode.title)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
|
|
@ -28,9 +28,11 @@ public struct EditAccountView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.environment(\.editMode, .constant(.active))
|
.environment(\.editMode, .constant(.active))
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
.scrollDismissesKeyboard(.immediately)
|
.scrollDismissesKeyboard(.immediately)
|
||||||
|
#endif
|
||||||
.navigationTitle("account.edit.navigation-title")
|
.navigationTitle("account.edit.navigation-title")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
@ -56,7 +58,9 @@ public struct EditAccountView: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
|
@ -69,7 +73,9 @@ public struct EditAccountView: View {
|
||||||
TextField("account.edit.about", text: $viewModel.note, axis: .vertical)
|
TextField("account.edit.about", text: $viewModel.note, axis: .vertical)
|
||||||
.frame(maxHeight: 150)
|
.frame(maxHeight: 150)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var postSettingsSection: some View {
|
private var postSettingsSection: some View {
|
||||||
|
@ -89,7 +95,9 @@ public struct EditAccountView: View {
|
||||||
Label("account.edit.post-settings.sensitive", systemImage: "eye")
|
Label("account.edit.post-settings.sensitive", systemImage: "eye")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var accountSection: some View {
|
private var accountSection: some View {
|
||||||
|
@ -104,7 +112,9 @@ public struct EditAccountView: View {
|
||||||
Label("account.edit.account-settings.discoverable", systemImage: "magnifyingglass")
|
Label("account.edit.account-settings.discoverable", systemImage: "magnifyingglass")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var fieldsSection: some View {
|
private var fieldsSection: some View {
|
||||||
|
@ -138,7 +148,9 @@ public struct EditAccountView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ToolbarContentBuilder
|
@ToolbarContentBuilder
|
||||||
|
|
|
@ -18,10 +18,14 @@ public struct EditRelationshipNoteView: View {
|
||||||
TextField("account.relation.note.edit.placeholder", text: $viewModel.note, axis: .vertical)
|
TextField("account.relation.note.edit.placeholder", text: $viewModel.note, axis: .vertical)
|
||||||
.frame(minHeight: 150, maxHeight: 150, alignment: .top)
|
.frame(minHeight: 150, maxHeight: 150, alignment: .top)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.navigationTitle("account.relation.note.edit")
|
.navigationTitle("account.relation.note.edit")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
|
|
@ -70,9 +70,11 @@ struct EditFilterView: View {
|
||||||
}
|
}
|
||||||
.navigationTitle(filter?.title ?? NSLocalizedString("filter.new", comment: ""))
|
.navigationTitle(filter?.title ?? NSLocalizedString("filter.new", comment: ""))
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.scrollDismissesKeyboard(.interactively)
|
.scrollDismissesKeyboard(.interactively)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if filter == nil {
|
if filter == nil {
|
||||||
focusedField = .title
|
focusedField = .title
|
||||||
|
@ -104,7 +106,9 @@ struct EditFilterView: View {
|
||||||
.disabled(expirySelection != .custom)
|
.disabled(expirySelection != .custom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
|
@ -118,7 +122,9 @@ struct EditFilterView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
|
|
||||||
if filter == nil, !title.isEmpty {
|
if filter == nil, !title.isEmpty {
|
||||||
Section {
|
Section {
|
||||||
|
@ -138,7 +144,9 @@ struct EditFilterView: View {
|
||||||
.buttonStyle(.borderedProminent)
|
.buttonStyle(.borderedProminent)
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.secondaryBackgroundColor)
|
.listRowBackground(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +200,9 @@ struct EditFilterView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var contextsSection: some View {
|
private var contextsSection: some View {
|
||||||
|
@ -214,7 +224,9 @@ struct EditFilterView: View {
|
||||||
}
|
}
|
||||||
.disabled(isSavingFilter)
|
.disabled(isSavingFilter)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +247,9 @@ struct EditFilterView: View {
|
||||||
}
|
}
|
||||||
.pickerStyle(.inline)
|
.pickerStyle(.inline)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var saveButton: some View {
|
private var saveButton: some View {
|
||||||
|
|
|
@ -54,7 +54,9 @@ public struct FiltersListView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
|
@ -62,15 +64,19 @@ public struct FiltersListView: View {
|
||||||
Label("filter.new", systemImage: "plus")
|
Label("filter.new", systemImage: "plus")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
.toolbar {
|
.toolbar {
|
||||||
toolbarContent
|
toolbarContent
|
||||||
}
|
}
|
||||||
.navigationTitle("filter.filters")
|
.navigationTitle("filter.filters")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.task {
|
.task {
|
||||||
do {
|
do {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
|
|
|
@ -8,6 +8,7 @@ let package = Package(
|
||||||
defaultLocalization: "en",
|
defaultLocalization: "en",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v17),
|
.iOS(.v17),
|
||||||
|
.visionOS(.v1),
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -96,7 +96,9 @@ public struct AppAccountsSelectorView: View {
|
||||||
AppAccountView(viewModel: viewModel)
|
AppAccountView(viewModel: viewModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
#if !os(visionOS)
|
||||||
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
|
|
||||||
if accountCreationEnabled {
|
if accountCreationEnabled {
|
||||||
Section {
|
Section {
|
||||||
|
@ -111,7 +113,9 @@ public struct AppAccountsSelectorView: View {
|
||||||
}
|
}
|
||||||
settingsButton
|
settingsButton
|
||||||
}
|
}
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
#if !os(visionOS)
|
||||||
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.listStyle(.insetGrouped)
|
.listStyle(.insetGrouped)
|
||||||
|
|
|
@ -8,6 +8,7 @@ let package = Package(
|
||||||
defaultLocalization: "en",
|
defaultLocalization: "en",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v17),
|
.iOS(.v17),
|
||||||
|
.visionOS(.v1),
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -46,7 +46,9 @@ public struct ConversationDetailView: View {
|
||||||
}
|
}
|
||||||
.padding(.horizontal, .layoutPadding)
|
.padding(.horizontal, .layoutPadding)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollDismissesKeyboard(.interactively)
|
.scrollDismissesKeyboard(.interactively)
|
||||||
|
#endif
|
||||||
.safeAreaInset(edge: .bottom) {
|
.safeAreaInset(edge: .bottom) {
|
||||||
inputTextView
|
inputTextView
|
||||||
}
|
}
|
||||||
|
@ -68,8 +70,10 @@ public struct ConversationDetailView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.primaryBackgroundColor)
|
.background(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .principal) {
|
ToolbarItem(placement: .principal) {
|
||||||
if viewModel.conversation.accounts.count == 1,
|
if viewModel.conversation.accounts.count == 1,
|
||||||
|
|
|
@ -43,7 +43,11 @@ struct ConversationMessageView: View {
|
||||||
routerPath.handleStatus(status: message, url: url)
|
routerPath.handleStatus(status: message, url: url)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
#if os(visionOS)
|
||||||
|
.background(isOwnMessage ? Material.ultraThick : Material.regular)
|
||||||
|
#else
|
||||||
.background(isOwnMessage ? theme.tintColor.opacity(0.2) : theme.secondaryBackgroundColor)
|
.background(isOwnMessage ? theme.tintColor.opacity(0.2) : theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.cornerRadius(8)
|
.cornerRadius(8)
|
||||||
.padding(.leading, isOwnMessage ? 24 : 0)
|
.padding(.leading, isOwnMessage ? 24 : 0)
|
||||||
.padding(.trailing, isOwnMessage ? 0 : 24)
|
.padding(.trailing, isOwnMessage ? 0 : 24)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import Accounts
|
|
||||||
import DesignSystem
|
import DesignSystem
|
||||||
import Env
|
import Env
|
||||||
import Models
|
import Models
|
||||||
|
@ -86,6 +85,8 @@ struct ConversationsListRow: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.hoverEffectDisabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var actionsView: some View {
|
private var actionsView: some View {
|
||||||
|
|
|
@ -81,8 +81,10 @@ public struct ConversationsListView: View {
|
||||||
}
|
}
|
||||||
.padding(.top, .layoutPadding)
|
.padding(.top, .layoutPadding)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.primaryBackgroundColor)
|
.background(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.navigationTitle("conversations.navigation-title")
|
.navigationTitle("conversations.navigation-title")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
|
|
@ -8,6 +8,7 @@ let package = Package(
|
||||||
defaultLocalization: "en",
|
defaultLocalization: "en",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v17),
|
.iOS(.v17),
|
||||||
|
.visionOS(.v1),
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -4,8 +4,13 @@ import UIKit
|
||||||
@Observable
|
@Observable
|
||||||
public class SceneDelegate: NSObject, UIWindowSceneDelegate, Sendable {
|
public class SceneDelegate: NSObject, UIWindowSceneDelegate, Sendable {
|
||||||
public var window: UIWindow?
|
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 windowWidth: CGFloat = UIScreen.main.bounds.size.width
|
||||||
public private(set) var windowHeight: CGFloat = UIScreen.main.bounds.size.height
|
public private(set) var windowHeight: CGFloat = UIScreen.main.bounds.size.height
|
||||||
|
#endif
|
||||||
|
|
||||||
public func scene(_ scene: UIScene,
|
public func scene(_ scene: UIScene,
|
||||||
willConnectTo _: UISceneSession,
|
willConnectTo _: UISceneSession,
|
||||||
|
@ -24,8 +29,13 @@ public class SceneDelegate: NSObject, UIWindowSceneDelegate, Sendable {
|
||||||
|
|
||||||
override public init() {
|
override public init() {
|
||||||
super.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
|
windowWidth = window?.bounds.size.width ?? UIScreen.main.bounds.size.width
|
||||||
windowHeight = window?.bounds.size.height ?? UIScreen.main.bounds.size.height
|
windowHeight = window?.bounds.size.height ?? UIScreen.main.bounds.size.height
|
||||||
|
#endif
|
||||||
Self.observedSceneDelegate.insert(self)
|
Self.observedSceneDelegate.insert(self)
|
||||||
_ = Self.observer // just for activating the lazy static property
|
_ = Self.observer // just for activating the lazy static property
|
||||||
}
|
}
|
||||||
|
@ -41,15 +51,26 @@ public class SceneDelegate: NSObject, UIWindowSceneDelegate, Sendable {
|
||||||
while true {
|
while true {
|
||||||
try? await Task.sleep(for: .seconds(0.1))
|
try? await Task.sleep(for: .seconds(0.1))
|
||||||
for delegate in observedSceneDelegate {
|
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
|
let newWidth = delegate.window?.bounds.size.width ?? UIScreen.main.bounds.size.width
|
||||||
if delegate.windowWidth != newWidth {
|
if delegate.windowWidth != newWidth {
|
||||||
delegate.windowWidth = newWidth
|
delegate.windowWidth = newWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
let newHeight = delegate.window?.bounds.size.height ?? UIScreen.main.bounds.size.height
|
let newHeight = delegate.window?.bounds.size.height ?? UIScreen.main.bounds.size.height
|
||||||
if delegate.windowHeight != newHeight {
|
if delegate.windowHeight != newHeight {
|
||||||
delegate.windowHeight = newHeight
|
delegate.windowHeight = newHeight
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ let package = Package(
|
||||||
defaultLocalization: "en",
|
defaultLocalization: "en",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v17),
|
.iOS(.v17),
|
||||||
|
.visionOS(.v1),
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -5,27 +5,42 @@ import UIKit
|
||||||
public class HapticManager {
|
public class HapticManager {
|
||||||
public static let shared: HapticManager = .init()
|
public static let shared: HapticManager = .init()
|
||||||
|
|
||||||
|
#if os(visionOS)
|
||||||
|
public enum FeedbackType: Int {
|
||||||
|
case success, warning, error
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public enum HapticType {
|
public enum HapticType {
|
||||||
case buttonPress
|
case buttonPress
|
||||||
case dataRefresh(intensity: CGFloat)
|
case dataRefresh(intensity: CGFloat)
|
||||||
|
#if os(visionOS)
|
||||||
|
case notification(_ type: FeedbackType)
|
||||||
|
#else
|
||||||
case notification(_ type: UINotificationFeedbackGenerator.FeedbackType)
|
case notification(_ type: UINotificationFeedbackGenerator.FeedbackType)
|
||||||
|
#endif
|
||||||
case tabSelection
|
case tabSelection
|
||||||
case timeline
|
case timeline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !os(visionOS)
|
||||||
private let selectionGenerator = UISelectionFeedbackGenerator()
|
private let selectionGenerator = UISelectionFeedbackGenerator()
|
||||||
private let impactGenerator = UIImpactFeedbackGenerator(style: .heavy)
|
private let impactGenerator = UIImpactFeedbackGenerator(style: .heavy)
|
||||||
private let notificationGenerator = UINotificationFeedbackGenerator()
|
private let notificationGenerator = UINotificationFeedbackGenerator()
|
||||||
|
#endif
|
||||||
|
|
||||||
private let userPreferences = UserPreferences.shared
|
private let userPreferences = UserPreferences.shared
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
|
#if !os(visionOS)
|
||||||
selectionGenerator.prepare()
|
selectionGenerator.prepare()
|
||||||
impactGenerator.prepare()
|
impactGenerator.prepare()
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public func fireHaptic(_ type: HapticType) {
|
public func fireHaptic(_ type: HapticType) {
|
||||||
|
#if !os(visionOS)
|
||||||
guard supportsHaptics else { return }
|
guard supportsHaptics else { return }
|
||||||
|
|
||||||
switch type {
|
switch type {
|
||||||
|
@ -50,6 +65,7 @@ public class HapticManager {
|
||||||
selectionGenerator.selectionChanged()
|
selectionGenerator.selectionChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public var supportsHaptics: Bool {
|
public var supportsHaptics: Bool {
|
||||||
|
|
|
@ -8,6 +8,7 @@ let package = Package(
|
||||||
defaultLocalization: "en",
|
defaultLocalization: "en",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v17),
|
.iOS(.v17),
|
||||||
|
.visionOS(.v1),
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -47,7 +47,9 @@ public struct ExploreView: View {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.secondaryBackgroundColor)
|
.listRowBackground(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
.id(UUID())
|
.id(UUID())
|
||||||
}
|
}
|
||||||
|
@ -55,7 +57,9 @@ public struct ExploreView: View {
|
||||||
EmptyView(iconName: "magnifyingglass",
|
EmptyView(iconName: "magnifyingglass",
|
||||||
title: "explore.search.title",
|
title: "explore.search.title",
|
||||||
message: "explore.search.message-\(client.server)")
|
message: "explore.search.message-\(client.server)")
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.secondaryBackgroundColor)
|
.listRowBackground(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
} else {
|
} else {
|
||||||
quickAccessView
|
quickAccessView
|
||||||
|
@ -90,8 +94,10 @@ public struct ExploreView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.listStyle(.plain)
|
.listStyle(.plain)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.navigationTitle("explore.navigation-title")
|
.navigationTitle("explore.navigation-title")
|
||||||
.searchable(text: $viewModel.searchQuery,
|
.searchable(text: $viewModel.searchQuery,
|
||||||
isPresented: $viewModel.isSearchPresented,
|
isPresented: $viewModel.isSearchPresented,
|
||||||
|
@ -140,7 +146,9 @@ public struct ExploreView: View {
|
||||||
}
|
}
|
||||||
.scrollIndicators(.never)
|
.scrollIndicators(.never)
|
||||||
.listRowInsets(EdgeInsets())
|
.listRowInsets(EdgeInsets())
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.secondaryBackgroundColor)
|
.listRowBackground(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +158,9 @@ public struct ExploreView: View {
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
.redacted(reason: .placeholder)
|
.redacted(reason: .placeholder)
|
||||||
.allowsHitTesting(false)
|
.allowsHitTesting(false)
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +171,9 @@ public struct ExploreView: View {
|
||||||
ForEach(results.accounts) { account in
|
ForEach(results.accounts) { account in
|
||||||
if let relationship = results.relationships.first(where: { $0.id == account.id }) {
|
if let relationship = results.relationships.first(where: { $0.id == account.id }) {
|
||||||
AccountsListRow(viewModel: .init(account: account, relationShip: relationship))
|
AccountsListRow(viewModel: .init(account: account, relationShip: relationship))
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,7 +182,9 @@ public struct ExploreView: View {
|
||||||
Section("explore.section.tags") {
|
Section("explore.section.tags") {
|
||||||
ForEach(results.hashtags) { tag in
|
ForEach(results.hashtags) { tag in
|
||||||
TagRowView(tag: tag)
|
TagRowView(tag: tag)
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.padding(.vertical, 4)
|
.padding(.vertical, 4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,7 +193,9 @@ public struct ExploreView: View {
|
||||||
Section("explore.section.posts") {
|
Section("explore.section.posts") {
|
||||||
ForEach(results.statuses) { status in
|
ForEach(results.statuses) { status in
|
||||||
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
|
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,14 +209,18 @@ public struct ExploreView: View {
|
||||||
{ account in
|
{ account in
|
||||||
if let relationship = viewModel.suggestedAccountsRelationShips.first(where: { $0.id == account.id }) {
|
if let relationship = viewModel.suggestedAccountsRelationShips.first(where: { $0.id == account.id }) {
|
||||||
AccountsListRow(viewModel: .init(account: account, relationShip: relationship))
|
AccountsListRow(viewModel: .init(account: account, relationShip: relationship))
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NavigationLink(value: RouterDestination.accountsList(accounts: viewModel.suggestedAccounts)) {
|
NavigationLink(value: RouterDestination.accountsList(accounts: viewModel.suggestedAccounts)) {
|
||||||
Text("see-more")
|
Text("see-more")
|
||||||
.foregroundColor(theme.tintColor)
|
.foregroundColor(theme.tintColor)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,14 +230,18 @@ public struct ExploreView: View {
|
||||||
.prefix(upTo: viewModel.trendingTags.count > 5 ? 5 : viewModel.trendingTags.count))
|
.prefix(upTo: viewModel.trendingTags.count > 5 ? 5 : viewModel.trendingTags.count))
|
||||||
{ tag in
|
{ tag in
|
||||||
TagRowView(tag: tag)
|
TagRowView(tag: tag)
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.padding(.vertical, 4)
|
.padding(.vertical, 4)
|
||||||
}
|
}
|
||||||
NavigationLink(value: RouterDestination.tagsList(tags: viewModel.trendingTags)) {
|
NavigationLink(value: RouterDestination.tagsList(tags: viewModel.trendingTags)) {
|
||||||
Text("see-more")
|
Text("see-more")
|
||||||
.foregroundColor(theme.tintColor)
|
.foregroundColor(theme.tintColor)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +251,9 @@ public struct ExploreView: View {
|
||||||
.prefix(upTo: viewModel.trendingStatuses.count > 3 ? 3 : viewModel.trendingStatuses.count))
|
.prefix(upTo: viewModel.trendingStatuses.count > 3 ? 3 : viewModel.trendingStatuses.count))
|
||||||
{ status in
|
{ status in
|
||||||
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
|
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +261,9 @@ public struct ExploreView: View {
|
||||||
Text("see-more")
|
Text("see-more")
|
||||||
.foregroundColor(theme.tintColor)
|
.foregroundColor(theme.tintColor)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +273,9 @@ public struct ExploreView: View {
|
||||||
.prefix(upTo: viewModel.trendingLinks.count > 3 ? 3 : viewModel.trendingLinks.count))
|
.prefix(upTo: viewModel.trendingLinks.count > 3 ? 3 : viewModel.trendingLinks.count))
|
||||||
{ card in
|
{ card in
|
||||||
StatusRowCardView(card: card)
|
StatusRowCardView(card: card)
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +283,9 @@ public struct ExploreView: View {
|
||||||
Text("see-more")
|
Text("see-more")
|
||||||
.foregroundColor(theme.tintColor)
|
.foregroundColor(theme.tintColor)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ let package = Package(
|
||||||
defaultLocalization: "en",
|
defaultLocalization: "en",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v17),
|
.iOS(.v17),
|
||||||
|
.visionOS(.v1),
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -65,7 +65,9 @@ public struct ListEditView: View {
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
.disabled(viewModel.isUpdating)
|
.disabled(viewModel.isUpdating)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollDismissesKeyboard(.immediately)
|
.scrollDismissesKeyboard(.immediately)
|
||||||
|
#endif
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
|
|
@ -8,6 +8,7 @@ let package = Package(
|
||||||
defaultLocalization: "en",
|
defaultLocalization: "en",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v17),
|
.iOS(.v17),
|
||||||
|
.visionOS(.v1),
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -8,6 +8,7 @@ let package = Package(
|
||||||
defaultLocalization: "en",
|
defaultLocalization: "en",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v17),
|
.iOS(.v17),
|
||||||
|
.visionOS(.v1),
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -8,6 +8,7 @@ let package = Package(
|
||||||
defaultLocalization: "en",
|
defaultLocalization: "en",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v17),
|
.iOS(.v17),
|
||||||
|
.visionOS(.v1),
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -8,6 +8,7 @@ let package = Package(
|
||||||
defaultLocalization: "en",
|
defaultLocalization: "en",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v17),
|
.iOS(.v17),
|
||||||
|
.visionOS(.v1),
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -83,8 +83,10 @@ public struct NotificationsListView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.primaryBackgroundColor)
|
.background(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.task {
|
.task {
|
||||||
viewModel.client = client
|
viewModel.client = client
|
||||||
viewModel.currentAccount = account
|
viewModel.currentAccount = account
|
||||||
|
@ -130,7 +132,12 @@ public struct NotificationsListView: View {
|
||||||
leading: .layoutPadding + 4,
|
leading: .layoutPadding + 4,
|
||||||
bottom: 12,
|
bottom: 12,
|
||||||
trailing: .layoutPadding))
|
trailing: .layoutPadding))
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
#if os(visionOS)
|
||||||
|
.listRowBackground(RoundedRectangle(cornerRadius: 8)
|
||||||
|
.foregroundStyle(Material.regular))
|
||||||
|
#else
|
||||||
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.redacted(reason: .placeholder)
|
.redacted(reason: .placeholder)
|
||||||
.allowsHitTesting(false)
|
.allowsHitTesting(false)
|
||||||
}
|
}
|
||||||
|
@ -140,7 +147,9 @@ public struct NotificationsListView: View {
|
||||||
EmptyView(iconName: "bell.slash",
|
EmptyView(iconName: "bell.slash",
|
||||||
title: "notifications.empty.title",
|
title: "notifications.empty.title",
|
||||||
message: "notifications.empty.message")
|
message: "notifications.empty.message")
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.listSectionSeparator(.hidden)
|
.listSectionSeparator(.hidden)
|
||||||
} else {
|
} else {
|
||||||
ForEach(notifications) { notification in
|
ForEach(notifications) { notification in
|
||||||
|
@ -152,8 +161,13 @@ public struct NotificationsListView: View {
|
||||||
leading: .layoutPadding + 4,
|
leading: .layoutPadding + 4,
|
||||||
bottom: 12,
|
bottom: 12,
|
||||||
trailing: .layoutPadding))
|
trailing: .layoutPadding))
|
||||||
|
#if os(visionOS)
|
||||||
|
.listRowBackground(RoundedRectangle(cornerRadius: 8)
|
||||||
|
.foregroundStyle(notification.type == .mention && lockedType != .mention ? Material.thick : Material.regular))
|
||||||
|
#else
|
||||||
.listRowBackground(notification.type == .mention && lockedType != .mention ?
|
.listRowBackground(notification.type == .mention && lockedType != .mention ?
|
||||||
theme.secondaryBackgroundColor : theme.primaryBackgroundColor)
|
theme.secondaryBackgroundColor : theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.id(notification.id)
|
.id(notification.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,7 +195,9 @@ public struct NotificationsListView: View {
|
||||||
await viewModel.fetchNotifications()
|
await viewModel.fetchNotifications()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.listSectionSeparator(.hidden)
|
.listSectionSeparator(.hidden)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,7 +212,9 @@ public struct NotificationsListView: View {
|
||||||
leading: .layoutPadding + 4,
|
leading: .layoutPadding + 4,
|
||||||
bottom: .layoutPadding,
|
bottom: .layoutPadding,
|
||||||
trailing: .layoutPadding))
|
trailing: .layoutPadding))
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var topPaddingView: some View {
|
private var topPaddingView: some View {
|
||||||
|
|
|
@ -8,6 +8,7 @@ let package = Package(
|
||||||
defaultLocalization: "en",
|
defaultLocalization: "en",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v17),
|
.iOS(.v17),
|
||||||
|
.visionOS(.v1),
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -59,7 +59,9 @@ public struct StatusDetailView: View {
|
||||||
.foregroundColor(theme.secondaryBackgroundColor)
|
.foregroundColor(theme.secondaryBackgroundColor)
|
||||||
.frame(minHeight: reader.frame(in: .local).size.height - statusHeight)
|
.frame(minHeight: reader.frame(in: .local).size.height - statusHeight)
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.secondaryBackgroundColor)
|
.listRowBackground(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.listRowInsets(.init())
|
.listRowInsets(.init())
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
|
|
||||||
|
@ -69,8 +71,10 @@ public struct StatusDetailView: View {
|
||||||
}
|
}
|
||||||
.environment(\.defaultMinListRowHeight, 1)
|
.environment(\.defaultMinListRowHeight, 1)
|
||||||
.listStyle(.plain)
|
.listStyle(.plain)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.primaryBackgroundColor)
|
.background(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.onChange(of: viewModel.scrollToId) { _, newValue in
|
.onChange(of: viewModel.scrollToId) { _, newValue in
|
||||||
if let newValue {
|
if let newValue {
|
||||||
viewModel.scrollToId = nil
|
viewModel.scrollToId = nil
|
||||||
|
@ -132,7 +136,9 @@ public struct StatusDetailView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.id(status.id)
|
.id(status.id)
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(viewModel.highlightRowColor)
|
.listRowBackground(viewModel.highlightRowColor)
|
||||||
|
#endif
|
||||||
.listRowInsets(.init(top: 12,
|
.listRowInsets(.init(top: 12,
|
||||||
leading: .layoutPadding,
|
leading: .layoutPadding,
|
||||||
bottom: 12,
|
bottom: 12,
|
||||||
|
@ -149,7 +155,9 @@ public struct StatusDetailView: View {
|
||||||
await viewModel.fetch()
|
await viewModel.fetch()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,13 +177,17 @@ public struct StatusDetailView: View {
|
||||||
}
|
}
|
||||||
.frame(height: 50)
|
.frame(height: 50)
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.secondaryBackgroundColor)
|
.listRowBackground(theme.secondaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.listRowInsets(.init())
|
.listRowInsets(.init())
|
||||||
}
|
}
|
||||||
|
|
||||||
private var topPaddingView: some View {
|
private var topPaddingView: some View {
|
||||||
HStack { EmptyView() }
|
HStack { EmptyView() }
|
||||||
|
#if !os(visionOS)
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
.listRowInsets(.init())
|
.listRowInsets(.init())
|
||||||
.frame(height: .layoutPadding)
|
.frame(height: .layoutPadding)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#if !os(visionOS)
|
||||||
import DesignSystem
|
import DesignSystem
|
||||||
import GiphyUISDK
|
import GiphyUISDK
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
@ -49,3 +50,4 @@ struct GifPickerView: UIViewControllerRepresentable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import DesignSystem
|
import DesignSystem
|
||||||
import Env
|
import Env
|
||||||
|
#if !os(visionOS)
|
||||||
import GiphyUISDK
|
import GiphyUISDK
|
||||||
|
#endif
|
||||||
import Models
|
import Models
|
||||||
import NukeUI
|
import NukeUI
|
||||||
import PhotosUI
|
import PhotosUI
|
||||||
|
@ -29,186 +31,220 @@ struct StatusEditorAccessoryView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@Bindable var viewModel = focusedSEVM
|
@Bindable var viewModel = focusedSEVM
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
|
#if os(visionOS)
|
||||||
|
HStack {
|
||||||
|
contentView
|
||||||
|
}
|
||||||
|
.frame(height: 24)
|
||||||
|
.padding(16)
|
||||||
|
.background(.ultraThinMaterial)
|
||||||
|
.cornerRadius(8)
|
||||||
|
#else
|
||||||
Divider()
|
Divider()
|
||||||
HStack {
|
HStack {
|
||||||
ScrollView(.horizontal) {
|
contentView
|
||||||
HStack(alignment: .center, spacing: 16) {
|
|
||||||
Menu {
|
|
||||||
Button {
|
|
||||||
isPhotosPickerPresented = true
|
|
||||||
} label: {
|
|
||||||
Label("status.editor.photo-library", systemImage: "photo")
|
|
||||||
}
|
|
||||||
#if !targetEnvironment(macCatalyst)
|
|
||||||
Button {
|
|
||||||
isCameraPickerPresented = true
|
|
||||||
} label: {
|
|
||||||
Label("status.editor.camera-picker", systemImage: "camera")
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
Button {
|
|
||||||
isFileImporterPresented = true
|
|
||||||
} label: {
|
|
||||||
Label("status.editor.browse-file", systemImage: "folder")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
isGIFPickerPresented = true
|
|
||||||
} label: {
|
|
||||||
Label("GIPHY", systemImage: "party.popper")
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
if viewModel.isMediasLoading {
|
|
||||||
ProgressView()
|
|
||||||
} else {
|
|
||||||
Image(systemName: "photo.on.rectangle.angled")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.photosPicker(isPresented: $isPhotosPickerPresented,
|
|
||||||
selection: $viewModel.mediaPickers,
|
|
||||||
maxSelectionCount: 4,
|
|
||||||
matching: .any(of: [.images, .videos]),
|
|
||||||
photoLibrary: .shared())
|
|
||||||
.fileImporter(isPresented: $isFileImporterPresented,
|
|
||||||
allowedContentTypes: [.image, .video],
|
|
||||||
allowsMultipleSelection: true)
|
|
||||||
{ result in
|
|
||||||
if let urls = try? result.get() {
|
|
||||||
viewModel.processURLs(urls: urls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.fullScreenCover(isPresented: $isCameraPickerPresented, content: {
|
|
||||||
StatusEditorCameraPickerView(selectedImage: .init(get: {
|
|
||||||
nil
|
|
||||||
}, set: { image in
|
|
||||||
if let image {
|
|
||||||
viewModel.processCameraPhoto(image: image)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.background(.black)
|
|
||||||
})
|
|
||||||
.sheet(isPresented: $isGIFPickerPresented, content: {
|
|
||||||
GifPickerView { url in
|
|
||||||
GPHCache.shared.downloadAssetData(url) { data, _ in
|
|
||||||
guard let data else { return }
|
|
||||||
viewModel.processGIFData(data: data)
|
|
||||||
}
|
|
||||||
isGIFPickerPresented = false
|
|
||||||
} onShouldDismissGifPicker: {
|
|
||||||
isGIFPickerPresented = false
|
|
||||||
}
|
|
||||||
.presentationDetents([.medium, .large])
|
|
||||||
})
|
|
||||||
.accessibilityLabel("accessibility.editor.button.attach-photo")
|
|
||||||
.disabled(viewModel.showPoll)
|
|
||||||
|
|
||||||
Button {
|
|
||||||
// all SEVM have the same visibility value
|
|
||||||
followUpSEVMs.append(StatusEditorViewModel(mode: .new(visibility: focusedSEVM.visibility)))
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "arrowshape.turn.up.left.circle.fill")
|
|
||||||
}
|
|
||||||
.disabled(!canAddNewSEVM)
|
|
||||||
|
|
||||||
Button {
|
|
||||||
withAnimation {
|
|
||||||
viewModel.showPoll.toggle()
|
|
||||||
viewModel.resetPollDefaults()
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "chart.bar")
|
|
||||||
}
|
|
||||||
.accessibilityLabel("accessibility.editor.button.poll")
|
|
||||||
.disabled(viewModel.shouldDisablePollButton)
|
|
||||||
|
|
||||||
Button {
|
|
||||||
withAnimation {
|
|
||||||
viewModel.spoilerOn.toggle()
|
|
||||||
}
|
|
||||||
isSpoilerTextFocused = viewModel.id
|
|
||||||
} label: {
|
|
||||||
Image(systemName: viewModel.spoilerOn ? "exclamationmark.triangle.fill" : "exclamationmark.triangle")
|
|
||||||
}
|
|
||||||
.accessibilityLabel("accessibility.editor.button.spoiler")
|
|
||||||
|
|
||||||
if !viewModel.mode.isInShareExtension {
|
|
||||||
Button {
|
|
||||||
isDraftsSheetDisplayed = true
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "archivebox")
|
|
||||||
}
|
|
||||||
.accessibilityLabel("accessibility.editor.button.drafts")
|
|
||||||
.popover(isPresented: $isDraftsSheetDisplayed) {
|
|
||||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
|
||||||
draftsListView
|
|
||||||
.presentationDetents([.medium])
|
|
||||||
} else {
|
|
||||||
draftsListView
|
|
||||||
.frame(width: 400, height: 500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !viewModel.customEmojiContainer.isEmpty {
|
|
||||||
Button {
|
|
||||||
isCustomEmojisSheetDisplay = true
|
|
||||||
} label: {
|
|
||||||
// This is a workaround for an apparent bug in the `face.smiling` SF Symbol.
|
|
||||||
// See https://github.com/Dimillian/IceCubesApp/issues/1193
|
|
||||||
let customEmojiSheetIconName = colorScheme == .light ? "face.smiling" : "face.smiling.inverse"
|
|
||||||
Image(systemName: customEmojiSheetIconName)
|
|
||||||
}
|
|
||||||
.accessibilityLabel("accessibility.editor.button.custom-emojis")
|
|
||||||
.popover(isPresented: $isCustomEmojisSheetDisplay) {
|
|
||||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
|
||||||
customEmojisSheet
|
|
||||||
} else {
|
|
||||||
customEmojisSheet
|
|
||||||
.frame(width: 400, height: 500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
isLanguageSheetDisplayed.toggle()
|
|
||||||
} label: {
|
|
||||||
if let language = viewModel.selectedLanguage {
|
|
||||||
Text(language.uppercased())
|
|
||||||
} else {
|
|
||||||
Image(systemName: "globe")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.accessibilityLabel("accessibility.editor.button.language")
|
|
||||||
.popover(isPresented: $isLanguageSheetDisplayed) {
|
|
||||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
|
||||||
languageSheetView
|
|
||||||
} else {
|
|
||||||
languageSheetView
|
|
||||||
.frame(width: 400, height: 500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if preferences.isOpenAIEnabled {
|
|
||||||
AIMenu.disabled(!viewModel.canPost)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.horizontal, .layoutPadding)
|
|
||||||
}
|
|
||||||
Spacer()
|
|
||||||
characterCountView
|
|
||||||
.padding(.trailing, .layoutPadding)
|
|
||||||
}
|
}
|
||||||
.frame(height: 20)
|
.frame(height: 20)
|
||||||
.padding(.vertical, 12)
|
.padding(.vertical, 12)
|
||||||
.background(.ultraThinMaterial)
|
.background(.ultraThinMaterial)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
viewModel.setInitialLanguageSelection(preference: preferences.recentlyUsedLanguages.first ?? preferences.serverPreferences?.postLanguage)
|
viewModel.setInitialLanguageSelection(preference: preferences.recentlyUsedLanguages.first ?? preferences.serverPreferences?.postLanguage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private var contentView: some View {
|
||||||
|
#if os(visionOS)
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
actionsView
|
||||||
|
characterCountView
|
||||||
|
.padding(.leading, 16)
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ScrollView(.horizontal) {
|
||||||
|
HStack(alignment: .center, spacing: 16) {
|
||||||
|
actionsView
|
||||||
|
}
|
||||||
|
.padding(.horizontal, .layoutPadding)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
characterCountView
|
||||||
|
.padding(.trailing, .layoutPadding)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private var actionsView: some View {
|
||||||
|
@Bindable var viewModel = focusedSEVM
|
||||||
|
Menu {
|
||||||
|
Button {
|
||||||
|
isPhotosPickerPresented = true
|
||||||
|
} label: {
|
||||||
|
Label("status.editor.photo-library", systemImage: "photo")
|
||||||
|
}
|
||||||
|
#if !targetEnvironment(macCatalyst)
|
||||||
|
Button {
|
||||||
|
isCameraPickerPresented = true
|
||||||
|
} label: {
|
||||||
|
Label("status.editor.camera-picker", systemImage: "camera")
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Button {
|
||||||
|
isFileImporterPresented = true
|
||||||
|
} label: {
|
||||||
|
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()
|
||||||
|
} else {
|
||||||
|
Image(systemName: "photo.on.rectangle.angled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.photosPicker(isPresented: $isPhotosPickerPresented,
|
||||||
|
selection: $viewModel.mediaPickers,
|
||||||
|
maxSelectionCount: 4,
|
||||||
|
matching: .any(of: [.images, .videos]),
|
||||||
|
photoLibrary: .shared())
|
||||||
|
.fileImporter(isPresented: $isFileImporterPresented,
|
||||||
|
allowedContentTypes: [.image, .video],
|
||||||
|
allowsMultipleSelection: true)
|
||||||
|
{ result in
|
||||||
|
if let urls = try? result.get() {
|
||||||
|
viewModel.processURLs(urls: urls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.fullScreenCover(isPresented: $isCameraPickerPresented, content: {
|
||||||
|
StatusEditorCameraPickerView(selectedImage: .init(get: {
|
||||||
|
nil
|
||||||
|
}, set: { image in
|
||||||
|
if let image {
|
||||||
|
viewModel.processCameraPhoto(image: image)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.background(.black)
|
||||||
|
})
|
||||||
|
.sheet(isPresented: $isGIFPickerPresented, content: {
|
||||||
|
#if !os(visionOS)
|
||||||
|
GifPickerView { url in
|
||||||
|
GPHCache.shared.downloadAssetData(url) { data, _ in
|
||||||
|
guard let data else { return }
|
||||||
|
viewModel.processGIFData(data: data)
|
||||||
|
}
|
||||||
|
isGIFPickerPresented = false
|
||||||
|
} onShouldDismissGifPicker: {
|
||||||
|
isGIFPickerPresented = false
|
||||||
|
}
|
||||||
|
.presentationDetents([.medium, .large])
|
||||||
|
#else
|
||||||
|
EmptyView()
|
||||||
|
#endif
|
||||||
|
})
|
||||||
|
.accessibilityLabel("accessibility.editor.button.attach-photo")
|
||||||
|
.disabled(viewModel.showPoll)
|
||||||
|
|
||||||
|
Button {
|
||||||
|
// all SEVM have the same visibility value
|
||||||
|
followUpSEVMs.append(StatusEditorViewModel(mode: .new(visibility: focusedSEVM.visibility)))
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "arrowshape.turn.up.left.circle.fill")
|
||||||
|
}
|
||||||
|
.disabled(!canAddNewSEVM)
|
||||||
|
|
||||||
|
Button {
|
||||||
|
withAnimation {
|
||||||
|
viewModel.showPoll.toggle()
|
||||||
|
viewModel.resetPollDefaults()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "chart.bar")
|
||||||
|
}
|
||||||
|
.accessibilityLabel("accessibility.editor.button.poll")
|
||||||
|
.disabled(viewModel.shouldDisablePollButton)
|
||||||
|
|
||||||
|
Button {
|
||||||
|
withAnimation {
|
||||||
|
viewModel.spoilerOn.toggle()
|
||||||
|
}
|
||||||
|
isSpoilerTextFocused = viewModel.id
|
||||||
|
} label: {
|
||||||
|
Image(systemName: viewModel.spoilerOn ? "exclamationmark.triangle.fill" : "exclamationmark.triangle")
|
||||||
|
}
|
||||||
|
.accessibilityLabel("accessibility.editor.button.spoiler")
|
||||||
|
|
||||||
|
if !viewModel.mode.isInShareExtension {
|
||||||
|
Button {
|
||||||
|
isDraftsSheetDisplayed = true
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "archivebox")
|
||||||
|
}
|
||||||
|
.accessibilityLabel("accessibility.editor.button.drafts")
|
||||||
|
.popover(isPresented: $isDraftsSheetDisplayed) {
|
||||||
|
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||||
|
draftsListView
|
||||||
|
.presentationDetents([.medium])
|
||||||
|
} else {
|
||||||
|
draftsListView
|
||||||
|
.frame(width: 400, height: 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !viewModel.customEmojiContainer.isEmpty {
|
||||||
|
Button {
|
||||||
|
isCustomEmojisSheetDisplay = true
|
||||||
|
} label: {
|
||||||
|
// This is a workaround for an apparent bug in the `face.smiling` SF Symbol.
|
||||||
|
// See https://github.com/Dimillian/IceCubesApp/issues/1193
|
||||||
|
let customEmojiSheetIconName = colorScheme == .light ? "face.smiling" : "face.smiling.inverse"
|
||||||
|
Image(systemName: customEmojiSheetIconName)
|
||||||
|
}
|
||||||
|
.accessibilityLabel("accessibility.editor.button.custom-emojis")
|
||||||
|
.popover(isPresented: $isCustomEmojisSheetDisplay) {
|
||||||
|
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||||
|
customEmojisSheet
|
||||||
|
} else {
|
||||||
|
customEmojisSheet
|
||||||
|
.frame(width: 400, height: 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
isLanguageSheetDisplayed.toggle()
|
||||||
|
} label: {
|
||||||
|
if let language = viewModel.selectedLanguage {
|
||||||
|
Text(language.uppercased())
|
||||||
|
} else {
|
||||||
|
Image(systemName: "globe")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.accessibilityLabel("accessibility.editor.button.language")
|
||||||
|
.popover(isPresented: $isLanguageSheetDisplayed) {
|
||||||
|
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||||
|
languageSheetView
|
||||||
|
} else {
|
||||||
|
languageSheetView
|
||||||
|
.frame(width: 400, height: 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if preferences.isOpenAIEnabled {
|
||||||
|
AIMenu.disabled(!viewModel.canPost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var canAddNewSEVM: Bool {
|
private var canAddNewSEVM: Bool {
|
||||||
guard followUpSEVMs.count < 5 else { return false }
|
guard followUpSEVMs.count < 5 else { return false }
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,9 @@ struct StatusEditorCameraPickerView: UIViewControllerRepresentable {
|
||||||
|
|
||||||
func makeUIViewController(context: Context) -> UIImagePickerController {
|
func makeUIViewController(context: Context) -> UIImagePickerController {
|
||||||
let imagePicker = UIImagePickerController()
|
let imagePicker = UIImagePickerController()
|
||||||
|
#if !os(visionOS)
|
||||||
imagePicker.sourceType = .camera
|
imagePicker.sourceType = .camera
|
||||||
|
#endif
|
||||||
imagePicker.delegate = context.coordinator
|
imagePicker.delegate = context.coordinator
|
||||||
return imagePicker
|
return imagePicker
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import Accounts
|
|
||||||
import AppAccount
|
import AppAccount
|
||||||
import DesignSystem
|
import DesignSystem
|
||||||
import Env
|
import Env
|
||||||
|
@ -53,7 +52,9 @@ struct StatusEditorCoreView: View {
|
||||||
}
|
}
|
||||||
.opacity(editorFocusState == assignedFocusState ? 1 : 0.6)
|
.opacity(editorFocusState == assignedFocusState ? 1 : 0.6)
|
||||||
}
|
}
|
||||||
|
#if !os(visionOS)
|
||||||
.background(theme.primaryBackgroundColor)
|
.background(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.focused($editorFocusState, equals: assignedFocusState)
|
.focused($editorFocusState, equals: assignedFocusState)
|
||||||
.onAppear { setupViewModel() }
|
.onAppear { setupViewModel() }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import Accounts
|
|
||||||
import AppAccount
|
import AppAccount
|
||||||
import DesignSystem
|
import DesignSystem
|
||||||
import EmojiText
|
import EmojiText
|
||||||
|
@ -74,13 +73,21 @@ public struct StatusEditorView: View {
|
||||||
.scrollPosition(id: $scrollID, anchor: .top)
|
.scrollPosition(id: $scrollID, anchor: .top)
|
||||||
.animation(.bouncy(duration: 0.3), value: editorFocusState)
|
.animation(.bouncy(duration: 0.3), value: editorFocusState)
|
||||||
.animation(.bouncy(duration: 0.3), value: followUpSEVMs)
|
.animation(.bouncy(duration: 0.3), value: followUpSEVMs)
|
||||||
|
#if !os(visionOS)
|
||||||
.background(theme.primaryBackgroundColor)
|
.background(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.safeAreaInset(edge: .bottom) {
|
.safeAreaInset(edge: .bottom) {
|
||||||
StatusEditorAutoCompleteView(viewModel: focusedSEVM)
|
StatusEditorAutoCompleteView(viewModel: focusedSEVM)
|
||||||
}
|
}
|
||||||
|
#if os(visionOS)
|
||||||
|
.ornament(attachmentAnchor: .scene(.bottom)) {
|
||||||
|
StatusEditorAccessoryView(isSpoilerTextFocused: $isSpoilerTextFocused, focusedSEVM: focusedSEVM, followUpSEVMs: $followUpSEVMs)
|
||||||
|
}
|
||||||
|
#else
|
||||||
.safeAreaInset(edge: .bottom) {
|
.safeAreaInset(edge: .bottom) {
|
||||||
StatusEditorAccessoryView(isSpoilerTextFocused: $isSpoilerTextFocused, focusedSEVM: focusedSEVM, followUpSEVMs: $followUpSEVMs)
|
StatusEditorAccessoryView(isSpoilerTextFocused: $isSpoilerTextFocused, focusedSEVM: focusedSEVM, followUpSEVMs: $followUpSEVMs)
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
.accessibilitySortPriority(1) // Ensure that all elements inside the `ScrollView` occur earlier than the accessory views
|
.accessibilitySortPriority(1) // Ensure that all elements inside the `ScrollView` occur earlier than the accessory views
|
||||||
.navigationTitle(focusedSEVM.mode.title)
|
.navigationTitle(focusedSEVM.mode.title)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
|
|
@ -149,7 +149,12 @@ public struct StatusRowView: View {
|
||||||
StatusRowSwipeView(viewModel: viewModel, mode: .leading)
|
StatusRowSwipeView(viewModel: viewModel, mode: .leading)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.listRowBackground(viewModel.highlightRowColor)
|
#if os(visionOS)
|
||||||
|
.listRowBackground(RoundedRectangle(cornerRadius: 8)
|
||||||
|
.foregroundStyle(Material.regular))
|
||||||
|
#else
|
||||||
|
.listRowBackground(viewModel.highlightRowColor)
|
||||||
|
#endif
|
||||||
.listRowInsets(.init(top: 12,
|
.listRowInsets(.init(top: 12,
|
||||||
leading: .layoutPadding,
|
leading: .layoutPadding,
|
||||||
bottom: 12,
|
bottom: 12,
|
||||||
|
|
|
@ -22,11 +22,13 @@ public struct StatusRowMediaPreviewView: View {
|
||||||
@State private var isQuickLookLoading: Bool = false
|
@State private var isQuickLookLoading: Bool = false
|
||||||
|
|
||||||
var availableWidth: CGFloat {
|
var availableWidth: CGFloat {
|
||||||
|
#if !os(visionOS)
|
||||||
if UIDevice.current.userInterfaceIdiom == .phone &&
|
if UIDevice.current.userInterfaceIdiom == .phone &&
|
||||||
(UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight) || theme.statusDisplayStyle == .medium
|
(UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight) || theme.statusDisplayStyle == .medium
|
||||||
{
|
{
|
||||||
return sceneDelegate.windowWidth * 0.80
|
return sceneDelegate.windowWidth * 0.80
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return sceneDelegate.windowWidth
|
return sceneDelegate.windowWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ let package = Package(
|
||||||
defaultLocalization: "en",
|
defaultLocalization: "en",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v17),
|
.iOS(.v17),
|
||||||
|
.visionOS(.v1),
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -60,8 +60,10 @@ public struct TimelineView: View {
|
||||||
.id(client.id)
|
.id(client.id)
|
||||||
.environment(\.defaultMinListRowHeight, 1)
|
.environment(\.defaultMinListRowHeight, 1)
|
||||||
.listStyle(.plain)
|
.listStyle(.plain)
|
||||||
|
#if !os(visionOS)
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.primaryBackgroundColor)
|
.background(theme.primaryBackgroundColor)
|
||||||
|
#endif
|
||||||
.introspect(.list, on: .iOS(.v17)) { (collectionView: UICollectionView) in
|
.introspect(.list, on: .iOS(.v17)) { (collectionView: UICollectionView) in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.collectionView = collectionView
|
self.collectionView = collectionView
|
||||||
|
|
Loading…
Reference in a new issue