This commit is contained in:
Thomas Ricouard 2024-02-14 12:48:14 +01:00
parent 2d988d48c1
commit 1f858414d8
146 changed files with 1610 additions and 1637 deletions

View file

@ -30,13 +30,13 @@ struct AppView: View {
var body: some View {
#if os(visionOS)
tabBarView
#else
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
sidebarView
} else {
tabBarView
}
#else
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
sidebarView
} else {
tabBarView
}
#endif
}
@ -49,7 +49,7 @@ struct AppView: View {
} else if UIDevice.current.userInterfaceIdiom == .vision {
return Tab.visionOSTab()
}
return sidebarTabs.tabs.map{ $0.tab }
return sidebarTabs.tabs.map { $0.tab }
}
var tabBarView: some View {
@ -58,9 +58,9 @@ struct AppView: View {
}, set: { newTab in
if newTab == .post {
#if os(visionOS)
openWindow(value: WindowDestinationEditor.newStatusEditor(visibility: userPreferences.postVisibility))
openWindow(value: WindowDestinationEditor.newStatusEditor(visibility: userPreferences.postVisibility))
#else
appRouterPath.presentedSheet = .newStatusEditor(visibility: userPreferences.postVisibility)
appRouterPath.presentedSheet = .newStatusEditor(visibility: userPreferences.postVisibility)
#endif
return
}
@ -106,38 +106,38 @@ struct AppView: View {
}
#if !os(visionOS)
var sidebarView: some View {
SideBarView(selectedTab: $selectedTab,
popToRootTab: $popToRootTab,
tabs: availableTabs)
{
HStack(spacing: 0) {
TabView(selection: $selectedTab) {
ForEach(availableTabs) { tab in
tab
.makeContentView(selectedTab: $selectedTab, popToRootTab: $popToRootTab)
.tabItem {
tab.label
}
.tag(tab)
var sidebarView: some View {
SideBarView(selectedTab: $selectedTab,
popToRootTab: $popToRootTab,
tabs: availableTabs)
{
HStack(spacing: 0) {
TabView(selection: $selectedTab) {
ForEach(availableTabs) { tab in
tab
.makeContentView(selectedTab: $selectedTab, popToRootTab: $popToRootTab)
.tabItem {
tab.label
}
.tag(tab)
}
}
.introspect(.tabView, on: .iOS(.v17)) { (tabview: UITabBarController) in
tabview.tabBar.isHidden = horizontalSizeClass == .regular
tabview.customizableViewControllers = []
tabview.moreNavigationController.isNavigationBarHidden = true
}
if horizontalSizeClass == .regular,
appAccountsManager.currentClient.isAuth,
userPreferences.showiPadSecondaryColumn
{
Divider().edgesIgnoringSafeArea(.all)
notificationsSecondaryColumn
}
}
.introspect(.tabView, on: .iOS(.v17)) { (tabview: UITabBarController) in
tabview.tabBar.isHidden = horizontalSizeClass == .regular
tabview.customizableViewControllers = []
tabview.moreNavigationController.isNavigationBarHidden = true
}
if horizontalSizeClass == .regular,
appAccountsManager.currentClient.isAuth,
userPreferences.showiPadSecondaryColumn
{
Divider().edgesIgnoringSafeArea(.all)
notificationsSecondaryColumn
}
}
.environment(appRouterPath)
}
.environment(appRouterPath)
}
#endif
var notificationsSecondaryColumn: some View {

View file

@ -36,37 +36,37 @@ public struct ReportView: View {
.navigationTitle("report.title")
.navigationBarTitleDisplayMode(.inline)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollDismissesKeyboard(.immediately)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollDismissesKeyboard(.immediately)
#endif
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
isSendingReport = true
Task {
do {
let _: ReportSent =
try await client.post(endpoint: Statuses.report(accountId: status.account.id,
statusId: status.id,
comment: commentText))
dismiss()
isSendingReport = false
} catch {
isSendingReport = false
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
isSendingReport = true
Task {
do {
let _: ReportSent =
try await client.post(endpoint: Statuses.report(accountId: status.account.id,
statusId: status.id,
comment: commentText))
dismiss()
isSendingReport = false
} catch {
isSendingReport = false
}
}
} label: {
if isSendingReport {
ProgressView()
} else {
Text("report.action.send")
}
}
} label: {
if isSendingReport {
ProgressView()
} else {
Text("report.action.send")
}
}
}
CancelToolbarItem()
}
CancelToolbarItem()
}
}
}
}

View file

@ -18,7 +18,7 @@ private struct SafariRouter: ViewModifier {
@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 {
@ -52,78 +52,78 @@ private struct SafariRouter: ViewModifier {
return .systemAction
}
#if os(visionOS)
return .systemAction
return .systemAction
#else
return safariManager.open(url)
return safariManager.open(url)
#endif
#else
return .systemAction
#endif
}
}
#if !os(visionOS)
#if !os(visionOS)
.background {
WindowReader { window in
safariManager.windowScene = window.windowScene
}
}
#endif
#endif
}
}
#if !os(visionOS)
@MainActor
@Observable private class InAppSafariManager: NSObject, SFSafariViewControllerDelegate {
var windowScene: UIWindowScene?
let viewController: UIViewController = .init()
var window: UIWindow?
@MainActor
func open(_ url: URL) -> OpenURLAction.Result {
guard let windowScene else { return .systemAction }
@Observable private class InAppSafariManager: NSObject, SFSafariViewControllerDelegate {
var windowScene: UIWindowScene?
let viewController: UIViewController = .init()
var window: UIWindow?
window = setupWindow(windowScene: windowScene)
@MainActor
func open(_ url: URL) -> OpenURLAction.Result {
guard let windowScene else { return .systemAction }
let configuration = SFSafariViewController.Configuration()
configuration.entersReaderIfAvailable = UserPreferences.shared.inAppBrowserReaderView
window = setupWindow(windowScene: windowScene)
let safari = SFSafariViewController(url: url, configuration: configuration)
safari.preferredBarTintColor = UIColor(Theme.shared.primaryBackgroundColor)
safari.preferredControlTintColor = UIColor(Theme.shared.tintColor)
safari.delegate = self
let configuration = SFSafariViewController.Configuration()
configuration.entersReaderIfAvailable = UserPreferences.shared.inAppBrowserReaderView
DispatchQueue.main.async { [weak self] in
self?.viewController.present(safari, animated: true)
let safari = SFSafariViewController(url: url, configuration: configuration)
safari.preferredBarTintColor = UIColor(Theme.shared.primaryBackgroundColor)
safari.preferredControlTintColor = UIColor(Theme.shared.tintColor)
safari.delegate = self
DispatchQueue.main.async { [weak self] in
self?.viewController.present(safari, animated: true)
}
return .handled
}
return .handled
}
func setupWindow(windowScene: UIWindowScene) -> UIWindow {
let window = window ?? UIWindow(windowScene: windowScene)
func setupWindow(windowScene: UIWindowScene) -> UIWindow {
let window = window ?? UIWindow(windowScene: windowScene)
window.rootViewController = viewController
window.makeKeyAndVisible()
window.rootViewController = viewController
window.makeKeyAndVisible()
switch Theme.shared.selectedScheme {
case .dark:
window.overrideUserInterfaceStyle = .dark
case .light:
window.overrideUserInterfaceStyle = .light
}
switch Theme.shared.selectedScheme {
case .dark:
window.overrideUserInterfaceStyle = .dark
case .light:
window.overrideUserInterfaceStyle = .light
self.window = window
return window
}
self.window = window
return window
}
nonisolated func safariViewControllerDidFinish(_: SFSafariViewController) {
Task { @MainActor in
window?.resignKey()
window?.isHidden = false
window = nil
nonisolated func safariViewControllerDidFinish(_: SFSafariViewController) {
Task { @MainActor in
window?.resignKey()
window?.isHidden = false
window = nil
}
}
}
}
#endif
private struct WindowReader: UIViewRepresentable {

View file

@ -166,7 +166,7 @@ struct SideBarView<Content: View>: View {
.frame(width: .sidebarWidth)
.background(.thinMaterial)
})
Divider().edgesIgnoringSafeArea(.all)
Divider().edgesIgnoringSafeArea(.all)
}
content()
}

View file

@ -1,7 +1,7 @@
import SwiftUI
import Env
import AppAccount
import DesignSystem
import Env
import SwiftUI
@MainActor
struct NavigationSheet<Content: View>: View {

View file

@ -1,8 +1,8 @@
import SwiftUI
import Env
import AppAccount
import DesignSystem
import Env
import Network
import SwiftUI
@MainActor
struct NavigationTab<Content: View>: View {

View file

@ -60,9 +60,9 @@ struct NotificationsTab: View {
}
}
}
.onChange(of: selectedTab, { _, newValue in
.onChange(of: selectedTab) { _, _ in
clearNotifications()
})
}
.onChange(of: pushNotificationsService.handledNotification) { _, newValue in
if let newValue, let type = newValue.notification.supportedType {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {

View file

@ -28,26 +28,26 @@ struct AboutView: View {
List {
Section {
#if !targetEnvironment(macCatalyst) && !os(visionOS)
HStack {
Spacer()
Image(uiImage: .init(named: "AppIconAlternate0")!)
.resizable()
.frame(width: 50, height: 50)
.cornerRadius(4)
Image(uiImage: .init(named: "AppIconAlternate4")!)
.resizable()
.frame(width: 50, height: 50)
.cornerRadius(4)
Image(uiImage: .init(named: "AppIconAlternate17")!)
.resizable()
.frame(width: 50, height: 50)
.cornerRadius(4)
Image(uiImage: .init(named: "AppIconAlternate23")!)
.resizable()
.frame(width: 50, height: 50)
.cornerRadius(4)
Spacer()
}
HStack {
Spacer()
Image(uiImage: .init(named: "AppIconAlternate0")!)
.resizable()
.frame(width: 50, height: 50)
.cornerRadius(4)
Image(uiImage: .init(named: "AppIconAlternate4")!)
.resizable()
.frame(width: 50, height: 50)
.cornerRadius(4)
Image(uiImage: .init(named: "AppIconAlternate17")!)
.resizable()
.frame(width: 50, height: 50)
.cornerRadius(4)
Image(uiImage: .init(named: "AppIconAlternate23")!)
.resizable()
.frame(width: 50, height: 50)
.cornerRadius(4)
Spacer()
}
#endif
Link(destination: URL(string: "https://github.com/Dimillian/IceCubesApp/blob/main/PRIVACY.MD")!) {
Label("settings.support.privacy-policy", systemImage: "lock")
@ -107,14 +107,14 @@ struct AboutView: View {
}
.listStyle(.insetGrouped)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
.navigationTitle(Text("settings.about.title"))
.navigationBarTitleDisplayMode(.large)
.environment(\.openURL, OpenURLAction { url in
routerPath.handle(url: url)
})
.navigationTitle(Text("settings.about.title"))
.navigationBarTitleDisplayMode(.large)
.environment(\.openURL, OpenURLAction { url in
routerPath.handle(url: url)
})
}
@ViewBuilder

View file

@ -116,8 +116,8 @@ struct AccountSettingsView: View {
}
.navigationTitle(account.safeDisplayName)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
}
}

View file

@ -82,75 +82,75 @@ struct AddAccountView: View {
.navigationTitle("account.add.navigation-title")
.navigationBarTitleDisplayMode(.inline)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollDismissesKeyboard(.immediately)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollDismissesKeyboard(.immediately)
#endif
.toolbar {
if !appAccountsManager.availableAccounts.isEmpty {
CancelToolbarItem()
}
}
.onAppear {
isInstanceURLFieldFocused = true
Task {
let instances = await instanceSocialClient.fetchInstances(keyword: instanceName)
withAnimation {
self.instances = instances
.toolbar {
if !appAccountsManager.availableAccounts.isEmpty {
CancelToolbarItem()
}
}
isSigninIn = false
}
.onChange(of: instanceName) {
searchingTask.cancel()
searchingTask = Task {
try? await Task.sleep(for: .seconds(0.1))
guard !Task.isCancelled else { return }
let instances = await instanceSocialClient.fetchInstances(keyword: instanceName)
withAnimation {
self.instances = instances
}
}
getInstanceDetailTask.cancel()
getInstanceDetailTask = Task {
try? await Task.sleep(for: .seconds(0.1))
guard !Task.isCancelled else { return }
do {
// bare bones preflight for domain validity
let instanceDetailClient = Client(server: sanitizedName)
if
instanceDetailClient.server.contains("."),
instanceDetailClient.server.last != "."
{
let instance: Instance = try await instanceDetailClient.get(endpoint: Instances.instance)
withAnimation {
self.instance = instance
instanceName = sanitizedName // clean up the text box, principally to chop off the username if present so it's clear that you might not wind up siging in as the thing in the box
}
instanceFetchError = nil
} else {
instance = nil
instanceFetchError = nil
.onAppear {
isInstanceURLFieldFocused = true
Task {
let instances = await instanceSocialClient.fetchInstances(keyword: instanceName)
withAnimation {
self.instances = instances
}
}
isSigninIn = false
}
.onChange(of: instanceName) {
searchingTask.cancel()
searchingTask = Task {
try? await Task.sleep(for: .seconds(0.1))
guard !Task.isCancelled else { return }
let instances = await instanceSocialClient.fetchInstances(keyword: instanceName)
withAnimation {
self.instances = instances
}
}
getInstanceDetailTask.cancel()
getInstanceDetailTask = Task {
try? await Task.sleep(for: .seconds(0.1))
guard !Task.isCancelled else { return }
do {
// bare bones preflight for domain validity
let instanceDetailClient = Client(server: sanitizedName)
if
instanceDetailClient.server.contains("."),
instanceDetailClient.server.last != "."
{
let instance: Instance = try await instanceDetailClient.get(endpoint: Instances.instance)
withAnimation {
self.instance = instance
instanceName = sanitizedName // clean up the text box, principally to chop off the username if present so it's clear that you might not wind up siging in as the thing in the box
}
instanceFetchError = nil
} else {
instance = nil
instanceFetchError = nil
}
} catch _ as DecodingError {
instance = nil
instanceFetchError = "account.add.error.instance-not-supported"
} catch {
instance = nil
}
} catch _ as DecodingError {
instance = nil
instanceFetchError = "account.add.error.instance-not-supported"
} catch {
instance = nil
}
}
}
.onChange(of: scenePhase) { _, newValue in
switch newValue {
case .active:
isSigninIn = false
default:
break
.onChange(of: scenePhase) { _, newValue in
switch newValue {
case .active:
isSigninIn = false
default:
break
}
}
}
}
}
@ -214,9 +214,9 @@ struct AddAccountView: View {
.foregroundColor(.primary)
Spacer()
(Text("instance.list.users-\(formatAsNumber(instance.users))")
+ Text("")
+ Text("instance.list.posts-\(formatAsNumber(instance.statuses))"))
.foregroundStyle(theme.tintColor)
+ Text("")
+ Text("instance.list.posts-\(formatAsNumber(instance.statuses))"))
.foregroundStyle(theme.tintColor)
}
.padding(.bottom, 5)
Text(instance.info?.shortDescription?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "")
@ -263,7 +263,7 @@ struct AddAccountView: View {
.redacted(reason: .placeholder)
.allowsHitTesting(false)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
.listRowBackground(theme.primaryBackgroundColor)
#endif
}

View file

@ -5,8 +5,8 @@ import Models
import Network
import NukeUI
import SwiftUI
import UserNotifications
import Timeline
import UserNotifications
@MainActor
struct ContentSettingsView: View {
@ -41,7 +41,7 @@ struct ContentSettingsView: View {
}
}
}
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#endif
@ -142,8 +142,8 @@ struct ContentSettingsView: View {
}
.navigationTitle("settings.content.navigation-title")
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
}
}

View file

@ -36,11 +36,11 @@ struct DisplaySettingsView: View {
ZStack(alignment: .top) {
Form {
#if !os(visionOS)
StatusRowView(viewModel: previewStatusViewModel)
.allowsHitTesting(false)
.opacity(0)
.hidden()
themeSection
StatusRowView(viewModel: previewStatusViewModel)
.allowsHitTesting(false)
.opacity(0)
.hidden()
themeSection
#endif
fontSection
layoutSection
@ -49,35 +49,35 @@ struct DisplaySettingsView: View {
}
.navigationTitle("settings.display.navigation-title")
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
.task(id: localValues.tintColor) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.tintColor = localValues.tintColor
}
.task(id: localValues.primaryBackgroundColor) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.primaryBackgroundColor = localValues.primaryBackgroundColor
}
.task(id: localValues.secondaryBackgroundColor) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.secondaryBackgroundColor = localValues.secondaryBackgroundColor
}
.task(id: localValues.labelColor) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.labelColor = localValues.labelColor
}
.task(id: localValues.lineSpacing) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.lineSpacing = localValues.lineSpacing
}
.task(id: localValues.fontSizeScale) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.fontSizeScale = localValues.fontSizeScale
}
.task(id: localValues.tintColor) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.tintColor = localValues.tintColor
}
.task(id: localValues.primaryBackgroundColor) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.primaryBackgroundColor = localValues.primaryBackgroundColor
}
.task(id: localValues.secondaryBackgroundColor) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.secondaryBackgroundColor = localValues.secondaryBackgroundColor
}
.task(id: localValues.labelColor) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.labelColor = localValues.labelColor
}
.task(id: localValues.lineSpacing) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.lineSpacing = localValues.lineSpacing
}
.task(id: localValues.fontSizeScale) {
do { try await Task.sleep(for: .microseconds(500)) } catch {}
theme.fontSizeScale = localValues.fontSizeScale
}
#if !os(visionOS)
examplePost
examplePost
#endif
}
}

View file

@ -23,8 +23,8 @@ struct HapticSettingsView: View {
}
.navigationTitle("settings.haptic.navigation-title")
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
}
}

View file

@ -14,8 +14,8 @@ struct InstanceInfoView: View {
}
.navigationTitle("instance.info.navigation-title")
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
}
}

View file

@ -111,12 +111,12 @@ struct PushNotificationsView: View {
}
.navigationTitle("settings.push.navigation-title")
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
.task {
await subscription.fetchSubscription()
}
.task {
await subscription.fetchSubscription()
}
}
private func updateSubscription() {

View file

@ -1,8 +1,8 @@
import SwiftUI
import SwiftData
import Models
import Env
import DesignSystem
import Env
import Models
import SwiftData
import SwiftUI
struct RecenTagsSettingView: View {
@Environment(\.modelContext) private var context
@ -35,10 +35,10 @@ struct RecenTagsSettingView: View {
.navigationTitle("settings.general.recent-tags")
.scrollContentBackground(.hidden)
#if !os(visionOS)
.background(theme.secondaryBackgroundColor)
.background(theme.secondaryBackgroundColor)
#endif
.toolbar {
EditButton()
}
.toolbar {
EditButton()
}
}
}

View file

@ -1,8 +1,8 @@
import SwiftUI
import SwiftData
import Models
import Env
import DesignSystem
import Env
import Models
import SwiftData
import SwiftUI
struct RemoteTimelinesSettingView: View {
@Environment(\.modelContext) private var context
@ -36,10 +36,10 @@ struct RemoteTimelinesSettingView: View {
.navigationTitle("settings.general.remote-timelines")
.scrollContentBackground(.hidden)
#if !os(visionOS)
.background(theme.secondaryBackgroundColor)
.background(theme.secondaryBackgroundColor)
#endif
.toolbar {
EditButton()
}
.toolbar {
EditButton()
}
}
}

View file

@ -43,27 +43,27 @@ struct SettingsTabs: View {
}
.scrollContentBackground(.hidden)
#if !os(visionOS)
.background(theme.secondaryBackgroundColor)
.background(theme.secondaryBackgroundColor)
#endif
.navigationTitle(Text("settings.title"))
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(theme.primaryBackgroundColor.opacity(0.30), for: .navigationBar)
.toolbar {
if isModal {
ToolbarItem {
Button {
dismiss()
} label: {
Text("action.done").bold()
.navigationTitle(Text("settings.title"))
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(theme.primaryBackgroundColor.opacity(0.30), for: .navigationBar)
.toolbar {
if isModal {
ToolbarItem {
Button {
dismiss()
} label: {
Text("action.done").bold()
}
}
}
if UIDevice.current.userInterfaceIdiom == .pad, !preferences.showiPadSecondaryColumn, !isModal {
SecondaryColumnToolbarItem()
}
}
if UIDevice.current.userInterfaceIdiom == .pad, !preferences.showiPadSecondaryColumn, !isModal {
SecondaryColumnToolbarItem()
}
}
.withAppRouter()
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
.withAppRouter()
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
}
.onAppear {
routerPath.client = client

View file

@ -28,8 +28,8 @@ struct SidebarEntriesSettingsView: View {
.environment(\.editMode, .constant(.active))
.navigationTitle("settings.general.sidebarEntries")
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
}

View file

@ -69,24 +69,24 @@ struct SupportAppView: View {
}
.navigationTitle("settings.support.navigation-title")
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
.alert("settings.support.alert.title", isPresented: $purchaseSuccessDisplayed, actions: {
Button { purchaseSuccessDisplayed = false } label: { Text("alert.button.ok") }
}, message: {
Text("settings.support.alert.message")
})
.alert("alert.error", isPresented: $purchaseErrorDisplayed, actions: {
Button { purchaseErrorDisplayed = false } label: { Text("alert.button.ok") }
}, message: {
Text("settings.support.alert.error.message")
})
.onAppear {
loadingProducts = true
fetchStoreProducts()
refreshUserInfo()
}
.alert("settings.support.alert.title", isPresented: $purchaseSuccessDisplayed, actions: {
Button { purchaseSuccessDisplayed = false } label: { Text("alert.button.ok") }
}, message: {
Text("settings.support.alert.message")
})
.alert("alert.error", isPresented: $purchaseErrorDisplayed, actions: {
Button { purchaseErrorDisplayed = false } label: { Text("alert.button.ok") }
}, message: {
Text("settings.support.alert.error.message")
})
.onAppear {
loadingProducts = true
fetchStoreProducts()
refreshUserInfo()
}
}
private func purchase(product: StoreProduct) async {

View file

@ -70,8 +70,8 @@ struct SwipeActionsSettingsView: View {
}
.navigationTitle("settings.swipeactions.navigation-title")
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
}

View file

@ -52,8 +52,8 @@ struct TabbarEntriesSettingsView: View {
}
.navigationTitle("settings.general.tabbarEntries")
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
}
}

View file

@ -1,8 +1,8 @@
import SwiftUI
import SwiftData
import Models
import Env
import DesignSystem
import Env
import Models
import SwiftData
import SwiftUI
struct TagsGroupSettingView: View {
@Environment(\.modelContext) private var context
@ -41,10 +41,10 @@ struct TagsGroupSettingView: View {
.navigationTitle("timeline.filter.tag-groups")
.scrollContentBackground(.hidden)
#if !os(visionOS)
.background(theme.secondaryBackgroundColor)
.background(theme.secondaryBackgroundColor)
#endif
.toolbar {
EditButton()
}
.toolbar {
EditButton()
}
}
}

View file

@ -41,13 +41,13 @@ struct TranslationSettingsView: View {
}
.navigationTitle("settings.translation.navigation-title")
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
.onChange(of: apiKey) {
writeNewValue()
}
.onAppear(perform: updatePrefs)
.onChange(of: apiKey) {
writeNewValue()
}
.onAppear(perform: updatePrefs)
}
@ViewBuilder

View file

@ -71,7 +71,7 @@ enum Tab: Int, Identifiable, Hashable, CaseIterable, Codable {
case .links:
NavigationTab { TrendingLinksListView(cards: []) }
case .post:
VStack { }
VStack {}
case .other:
EmptyView()
}
@ -114,7 +114,6 @@ enum Tab: Int, Identifiable, Hashable, CaseIterable, Codable {
Label("explore.section.trending.links", systemImage: iconName)
case .other:
EmptyView()
}
}

View file

@ -61,20 +61,20 @@ struct EditTagGroupView: View {
)
.navigationBarTitleDisplayMode(.inline)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollDismissesKeyboard(.interactively)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollDismissesKeyboard(.interactively)
#endif
.toolbar {
CancelToolbarItem()
ToolbarItem(placement: .navigationBarTrailing) {
Button("action.save", action: { save() })
.disabled(!tagGroup.isValid)
.toolbar {
CancelToolbarItem()
ToolbarItem(placement: .navigationBarTrailing) {
Button("action.save", action: { save() })
.disabled(!tagGroup.isValid)
}
}
.onAppear {
focusedField = .title
}
}
.onAppear {
focusedField = .title
}
}
}

View file

@ -52,29 +52,29 @@ struct AddRemoteTimelineView: View {
.navigationTitle("timeline.add-remote.title")
.navigationBarTitleDisplayMode(.inline)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollDismissesKeyboard(.immediately)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollDismissesKeyboard(.immediately)
#endif
.toolbar {
CancelToolbarItem()
}
.onChange(of: instanceName) { _, newValue in
instanceNamePublisher.send(newValue)
}
.onReceive(instanceNamePublisher.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)) { newValue in
Task {
let client = Client(server: newValue)
instance = try? await client.get(endpoint: Instances.instance)
.toolbar {
CancelToolbarItem()
}
}
.onAppear {
isInstanceURLFieldFocused = true
let client = InstanceSocialClient()
Task {
instances = await client.fetchInstances(keyword: instanceName)
.onChange(of: instanceName) { _, newValue in
instanceNamePublisher.send(newValue)
}
.onReceive(instanceNamePublisher.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)) { newValue in
Task {
let client = Client(server: newValue)
instance = try? await client.get(endpoint: Instances.instance)
}
}
.onAppear {
isInstanceURLFieldFocused = true
let client = InstanceSocialClient()
Task {
instances = await client.fetchInstances(keyword: instanceName)
}
}
}
}
}

View file

@ -95,7 +95,7 @@ struct TimelineTab: View {
}
switch newValue {
case let .tagGroup(title, _, _):
if let group = tagGroups.first(where: { $0.title == title}) {
if let group = tagGroups.first(where: { $0.title == title }) {
selectedTagGroup = group
}
default:
@ -212,7 +212,7 @@ struct TimelineTab: View {
@ViewBuilder
private var pinButton: some View {
let index = pinnedFilters.firstIndex(where: { $0.id == timeline.id})
let index = pinnedFilters.firstIndex(where: { $0.id == timeline.id })
Button {
withAnimation {
if let index {
@ -222,7 +222,7 @@ struct TimelineTab: View {
}
}
} label: {
if index != nil {
if index != nil {
Label("status.action.unpin", systemImage: "pin.slash")
} else {
Label("status.action.pin", systemImage: "pin")

View file

@ -1,7 +1,7 @@
import SwiftUI
import Env
import AppAccount
import DesignSystem
import Env
import SwiftUI
@MainActor
struct ToolbarTab: ToolbarContent {
@ -17,7 +17,8 @@ struct ToolbarTab: ToolbarContent {
statusEditorToolbarItem(routerPath: routerPath,
visibility: userPreferences.postVisibility)
if UIDevice.current.userInterfaceIdiom != .pad ||
(UIDevice.current.userInterfaceIdiom == .pad && horizontalSizeClass == .compact) {
(UIDevice.current.userInterfaceIdiom == .pad && horizontalSizeClass == .compact)
{
ToolbarItem(placement: .navigationBarLeading) {
AppAccountsSelectorView(routerPath: routerPath)
}

View file

@ -24,7 +24,7 @@ extension NotificationService {
var _plaintext: Data?
do {
_plaintext = try AES.GCM.open(sealedBox, using: key)
} catch { }
} catch {}
guard let plaintext = _plaintext else {
return nil
}

View file

@ -2,11 +2,11 @@ import Account
import AppAccount
import DesignSystem
import Env
import Models
import Network
import StatusKit
import SwiftUI
import UIKit
import Models
class ShareViewController: UIViewController {
override func viewDidLoad() {

View file

@ -31,7 +31,7 @@ let package = Package(
.product(name: "Models", package: "Models"),
.product(name: "StatusKit", package: "StatusKit"),
.product(name: "Env", package: "Env"),
.product(name: "ButtonKit", package: "ButtonKit")
.product(name: "ButtonKit", package: "ButtonKit"),
],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency"),

View file

@ -28,16 +28,16 @@ public struct AccountDetailContextMenu: View {
Label("account.action.message", systemImage: "tray.full")
}
#if !targetEnvironment(macCatalyst)
#if !targetEnvironment(macCatalyst)
Divider()
#endif
#endif
if viewModel.relationship?.blocking == true {
Button {
Task {
do {
viewModel.relationship = try await client.post(endpoint: Accounts.unblock(id: account.id))
} catch { }
} catch {}
}
} label: {
Label("account.action.unblock", systemImage: "person.crop.circle.badge.exclamationmark")
@ -55,7 +55,7 @@ public struct AccountDetailContextMenu: View {
Task {
do {
viewModel.relationship = try await client.post(endpoint: Accounts.unmute(id: account.id))
} catch { }
} catch {}
}
} label: {
Label("account.action.unmute", systemImage: "speaker")
@ -67,7 +67,7 @@ public struct AccountDetailContextMenu: View {
Task {
do {
viewModel.relationship = try await client.post(endpoint: Accounts.mute(id: account.id, json: MuteData(duration: duration.rawValue)))
} catch { }
} catch {}
}
}
}
@ -86,7 +86,7 @@ public struct AccountDetailContextMenu: View {
viewModel.relationship = try await client.post(endpoint: Accounts.follow(id: account.id,
notify: false,
reblogs: relationship.showingReblogs))
} catch { }
} catch {}
}
} label: {
Label("account.action.notify-disable", systemImage: "bell.fill")
@ -98,7 +98,7 @@ public struct AccountDetailContextMenu: View {
viewModel.relationship = try await client.post(endpoint: Accounts.follow(id: account.id,
notify: true,
reblogs: relationship.showingReblogs))
} catch { }
} catch {}
}
} label: {
Label("account.action.notify-enable", systemImage: "bell")
@ -111,7 +111,7 @@ public struct AccountDetailContextMenu: View {
viewModel.relationship = try await client.post(endpoint: Accounts.follow(id: account.id,
notify: relationship.notifying,
reblogs: false))
} catch { }
} catch {}
}
} label: {
Label("account.action.reboosts-hide", image: "Rocket.Fill")
@ -123,7 +123,7 @@ public struct AccountDetailContextMenu: View {
viewModel.relationship = try await client.post(endpoint: Accounts.follow(id: account.id,
notify: relationship.notifying,
reblogs: true))
} catch { }
} catch {}
}
} label: {
Label("account.action.reboosts-show", image: "Rocket")
@ -131,9 +131,9 @@ public struct AccountDetailContextMenu: View {
}
}
#if !targetEnvironment(macCatalyst)
#if !targetEnvironment(macCatalyst)
Divider()
#endif
#endif
}
if let lang = preferences.serverPreferences?.postLanguage ?? Locale.current.language.languageCode?.identifier {
@ -164,7 +164,7 @@ public struct AccountDetailContextMenu: View {
}
#if !targetEnvironment(macCatalyst)
Divider()
Divider()
#endif
}
}

View file

@ -372,15 +372,15 @@ struct AccountDetailHeaderView: View {
.accessibilityElement(children: .contain)
.accessibilityLabel("accessibility.tabs.profile.fields.container.label")
#if os(visionOS)
.background(Material.thick)
.background(Material.thick)
#else
.background(theme.secondaryBackgroundColor)
.background(theme.secondaryBackgroundColor)
#endif
.cornerRadius(4)
.overlay(
RoundedRectangle(cornerRadius: 4)
.stroke(.gray.opacity(0.35), lineWidth: 1)
)
.cornerRadius(4)
.overlay(
RoundedRectangle(cornerRadius: 4)
.stroke(.gray.opacity(0.35), lineWidth: 1)
)
}
}
}

View file

@ -88,14 +88,14 @@ public struct AccountDetailView: View {
.environment(\.defaultMinListRowHeight, 1)
.listStyle(.plain)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor)
#endif
.onChange(of: scrollToTopSignal) {
withAnimation {
proxy.scrollTo(ScrollToView.Constants.scrollToTop, anchor: .top)
.onChange(of: scrollToTopSignal) {
withAnimation {
proxy.scrollTo(ScrollToView.Constants.scrollToTop, anchor: .top)
}
}
}
}
.onAppear {
guard reasons != .placeholder else { return }
@ -220,7 +220,6 @@ public struct AccountDetailView: View {
AvatarView(account.avatar, config: .badge)
.padding(.leading, -4)
.accessibilityLabel(account.safeDisplayName)
}
.accessibilityAddTraits(.isImage)
.buttonStyle(.plain)
@ -247,18 +246,18 @@ public struct AccountDetailView: View {
bottom: 0,
trailing: .layoutPadding))
.listRowSeparator(.hidden)
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#endif
#endif
ForEach(viewModel.pinned) { status in
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
}
Rectangle()
#if os(visionOS)
#if os(visionOS)
.fill(Color.clear)
#else
#else
.fill(theme.secondaryBackgroundColor)
#endif
#endif
.frame(height: 12)
.listRowInsets(.init())
.listRowSeparator(.hidden)
@ -288,7 +287,6 @@ public struct AccountDetailView: View {
routerPath.presentedSheet = .mentionStatusEditor(account: account,
visibility: preferences.postVisibility)
#endif
}
} label: {
Image(systemName: "arrowshape.turn.up.left")
@ -370,7 +368,7 @@ public struct AccountDetailView: View {
Task {
do {
viewModel.relationship = try await client.post(endpoint: Accounts.block(id: account.id))
} catch { }
} catch {}
}
}
}
@ -385,9 +383,9 @@ extension View {
func applyAccountDetailsRowStyle(theme: Theme) -> some View {
listRowInsets(.init())
.listRowSeparator(.hidden)
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#endif
#endif
}
}

View file

@ -151,7 +151,7 @@ import SwiftUI
self.familiarFollowers = familiarFollowers?.first?.accounts ?? []
}
func fetchNewestStatuses(pullToRefresh: Bool) async {
func fetchNewestStatuses(pullToRefresh _: Bool) async {
guard let client else { return }
do {
statusesState = .loading
@ -166,7 +166,7 @@ import SwiftUI
pinned: nil))
StatusDataControllerProvider.shared.updateDataControllers(for: statuses, client: client)
if selectedTab == .boosts {
boosts = statuses.filter{ $0.reblog != nil }
boosts = statuses.filter { $0.reblog != nil }
}
if selectedTab == .statuses {
pinned =
@ -197,17 +197,17 @@ import SwiftUI
case .statuses, .replies, .boosts, .media:
guard let lastId = statuses.last?.id else { return }
let newStatuses: [Status] =
try await client.get(endpoint: Accounts.statuses(id: accountId,
sinceId: lastId,
tag: nil,
onlyMedia: selectedTab == .media,
excludeReplies: selectedTab != .replies,
excludeReblogs: selectedTab != .boosts,
pinned: nil))
try await client.get(endpoint: Accounts.statuses(id: accountId,
sinceId: lastId,
tag: nil,
onlyMedia: selectedTab == .media,
excludeReplies: selectedTab != .replies,
excludeReblogs: selectedTab != .boosts,
pinned: nil))
statuses.append(contentsOf: newStatuses)
if selectedTab == .boosts {
let newBoosts = statuses.filter{ $0.reblog != nil }
self.boosts.append(contentsOf: newBoosts)
let newBoosts = statuses.filter { $0.reblog != nil }
boosts.append(contentsOf: newBoosts)
}
StatusDataControllerProvider.shared.updateDataControllers(for: newStatuses, client: client)
if selectedTab == .boosts {
@ -253,7 +253,8 @@ import SwiftUI
if let event = event as? StreamEventUpdate {
if event.status.account.id == currentAccount.account?.id {
if (event.status.inReplyToId == nil && selectedTab == .statuses) ||
(event.status.inReplyToId != nil && selectedTab == .replies) {
(event.status.inReplyToId != nil && selectedTab == .replies)
{
statuses.insert(event.status, at: 0)
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
}

View file

@ -89,9 +89,9 @@ public struct AccountsListView: View {
AccountsListRow(viewModel: .init(account: .placeholder(), relationShip: .placeholder()))
.redacted(reason: .placeholder)
.allowsHitTesting(false)
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#endif
#endif
}
case let .display(accounts, relationships, nextPageState):
if case .followers = viewModel.mode,
@ -125,9 +125,9 @@ public struct AccountsListView: View {
if let relationship = relationships.first(where: { $0.id == account.id }) {
AccountsListRow(viewModel: .init(account: account,
relationShip: relationship))
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#endif
#endif
}
}
}
@ -147,9 +147,9 @@ public struct AccountsListView: View {
case let .error(error):
Text(error.localizedDescription)
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#endif
#endif
}
}
}

View file

@ -1,8 +1,8 @@
import Models
import Network
import Observation
import SwiftUI
import OSLog
import SwiftUI
public enum AccountsListMode {
case following(accountId: String), followers(accountId: String)
@ -144,8 +144,6 @@ public enum AccountsListMode {
relationships: relationships,
nextPageState: .none)
}
} catch {
}
} catch {}
}
}

View file

@ -2,8 +2,8 @@ import DesignSystem
import Env
import Models
import Network
import SwiftUI
import NukeUI
import SwiftUI
@MainActor
public struct EditAccountView: View {
@ -14,7 +14,7 @@ public struct EditAccountView: View {
@State private var viewModel = EditAccountViewModel()
public init() { }
public init() {}
public var body: some View {
NavigationStack {
@ -31,24 +31,24 @@ public struct EditAccountView: View {
}
.environment(\.editMode, .constant(.active))
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollDismissesKeyboard(.immediately)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollDismissesKeyboard(.immediately)
#endif
.navigationTitle("account.edit.navigation-title")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
toolbarContent
}
.alert("account.edit.error.save.title",
isPresented: $viewModel.saveError,
actions: {
Button("alert.button.ok", action: {})
}, message: { Text("account.edit.error.save.message") })
.task {
viewModel.client = client
await viewModel.fetchAccount()
}
.navigationTitle("account.edit.navigation-title")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
toolbarContent
}
.alert("account.edit.error.save.title",
isPresented: $viewModel.saveError,
actions: {
Button("alert.button.ok", action: {})
}, message: { Text("account.edit.error.save.message") })
.task {
viewModel.client = client
await viewModel.fetchAccount()
}
}
}

View file

@ -1,9 +1,9 @@
import Models
import Network
import Observation
import SwiftUI
import PhotosUI
import StatusKit
import SwiftUI
@MainActor
@Observable class EditAccountViewModel {
@ -33,12 +33,13 @@ import StatusKit
var isPhotoPickerPresented: Bool = false {
didSet {
if !isPhotoPickerPresented && mediaPickers.isEmpty {
if !isPhotoPickerPresented, mediaPickers.isEmpty {
isChangingAvatar = false
isChangingHeader = false
}
}
}
var isChangingAvatar: Bool = false
var isChangingHeader: Bool = false
@ -113,11 +114,11 @@ import StatusKit
guard let client else { return false }
do {
let response = try await client.mediaUpload(endpoint: Accounts.updateCredentialsMedia,
version: .v1,
method: "PATCH",
mimeType: "image/jpeg",
filename: "header",
data: data)
version: .v1,
method: "PATCH",
mimeType: "image/jpeg",
filename: "header",
data: data)
return response?.statusCode == 200
} catch {
return false
@ -128,11 +129,11 @@ import StatusKit
guard let client else { return false }
do {
let response = try await client.mediaUpload(endpoint: Accounts.updateCredentialsMedia,
version: .v1,
method: "PATCH",
mimeType: "image/jpeg",
filename: "avatar",
data: data)
version: .v1,
method: "PATCH",
mimeType: "image/jpeg",
filename: "avatar",
data: data)
return response?.statusCode == 200
} catch {
return false
@ -145,8 +146,8 @@ import StatusKit
let compressor = StatusEditor.Compressor()
guard let compressedData = await compressor.compressImageFrom(url: imageFile.url),
let image = UIImage(data: compressedData),
let uploadData = try? await compressor.compressImageForUpload(image)
let image = UIImage(data: compressedData),
let uploadData = try? await compressor.compressImageForUpload(image)
else { return nil }
return uploadData

View file

@ -71,20 +71,20 @@ struct EditFilterView: View {
.navigationTitle(filter?.title ?? NSLocalizedString("filter.new", comment: ""))
.navigationBarTitleDisplayMode(.inline)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.scrollDismissesKeyboard(.interactively)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.scrollDismissesKeyboard(.interactively)
.background(theme.secondaryBackgroundColor)
#endif
.onAppear {
if filter == nil {
focusedField = .title
.onAppear {
if filter == nil {
focusedField = .title
}
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
saveButton
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
saveButton
}
}
}
}
private var expirySection: some View {

View file

@ -74,18 +74,18 @@ public struct FiltersListView: View {
.navigationTitle("filter.filters")
.navigationBarTitleDisplayMode(.inline)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
.task {
do {
isLoading = true
filters = try await client.get(endpoint: ServerFilters.filters, forceVersion: .v2)
isLoading = false
} catch {
isLoading = false
.task {
do {
isLoading = true
filters = try await client.get(endpoint: ServerFilters.filters, forceVersion: .v2)
isLoading = false
} catch {
isLoading = false
}
}
}
}
}

View file

@ -4,8 +4,8 @@ import Foundation
import Models
import Network
import Observation
import SwiftUI
import OSLog
import SwiftUI
@MainActor
@Observable public class FollowButtonViewModel {

View file

@ -1,7 +1,7 @@
import DesignSystem
import Env
import Models
import SwiftUI
import Env
public struct ListsListView: View {
@Environment(CurrentAccount.self) private var currentAccount
@ -43,4 +43,3 @@ public struct ListsListView: View {
.navigationBarTitleDisplayMode(.inline)
}
}

View file

@ -1,9 +1,9 @@
import StatusKit
import Network
import SwiftUI
import DesignSystem
import Env
import Models
import DesignSystem
import Network
import StatusKit
import SwiftUI
@MainActor
public struct AccountStatusesListView: View {
@ -24,27 +24,27 @@ public struct AccountStatusesListView: View {
}
.listStyle(.plain)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor)
#endif
.navigationTitle(viewModel.mode.title)
.navigationBarTitleDisplayMode(.inline)
.refreshable {
await viewModel.fetchNewestStatuses(pullToRefresh: true)
}
.task {
guard !isLoaded else { return }
viewModel.client = client
await viewModel.fetchNewestStatuses(pullToRefresh: false)
isLoaded = true
}
.onChange(of: client.id) { _, _ in
isLoaded = false
viewModel.client = client
Task {
.navigationTitle(viewModel.mode.title)
.navigationBarTitleDisplayMode(.inline)
.refreshable {
await viewModel.fetchNewestStatuses(pullToRefresh: true)
}
.task {
guard !isLoaded else { return }
viewModel.client = client
await viewModel.fetchNewestStatuses(pullToRefresh: false)
isLoaded = true
}
}
.onChange(of: client.id) { _, _ in
isLoaded = false
viewModel.client = client
Task {
await viewModel.fetchNewestStatuses(pullToRefresh: false)
isLoaded = true
}
}
}
}

View file

@ -1,13 +1,13 @@
import SwiftUI
import Models
import StatusKit
import Network
import Env
import Models
import Network
import StatusKit
import SwiftUI
@MainActor
@Observable
public class AccountStatusesListViewModel: StatusesFetcher {
public enum Mode {
public enum Mode {
case bookmarks, favorites
var title: LocalizedStringKey {
@ -40,7 +40,7 @@ public class AccountStatusesListViewModel: StatusesFetcher {
self.mode = mode
}
public func fetchNewestStatuses(pullToRefresh: Bool) async {
public func fetchNewestStatuses(pullToRefresh _: Bool) async {
guard let client else { return }
statusesState = .loading
do {
@ -63,11 +63,7 @@ public class AccountStatusesListViewModel: StatusesFetcher {
nextPageState: nextPage?.maxId != nil ? .hasNextPage : .none)
}
public func statusDidAppear(status: Status) {
public func statusDidAppear(status _: Status) {}
}
public func statusDidDisappear(status: Status) {
}
public func statusDidDisappear(status _: Status) {}
}

View file

@ -1,7 +1,7 @@
import DesignSystem
import Env
import Models
import SwiftUI
import Env
public struct FollowedTagsListView: View {
@Environment(CurrentAccount.self) private var currentAccount
@ -12,9 +12,9 @@ public struct FollowedTagsListView: View {
public var body: some View {
List(currentAccount.tags) { tag in
TagRowView(tag: tag)
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#endif
#endif
.padding(.vertical, 4)
}
.task {
@ -32,4 +32,3 @@ public struct FollowedTagsListView: View {
.navigationBarTitleDisplayMode(.inline)
}
}

View file

@ -97,11 +97,11 @@ public struct AppAccountsSelectorView: View {
}
addAccountButton
#if os(visionOS)
.foregroundStyle(theme.labelColor)
.foregroundStyle(theme.labelColor)
#endif
}
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
.listRowBackground(theme.primaryBackgroundColor)
#endif
if accountCreationEnabled {

View file

@ -71,35 +71,35 @@ public struct ConversationDetailView: View {
}
.navigationBarTitleDisplayMode(.inline)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor)
#endif
.toolbar {
ToolbarItem(placement: .principal) {
if viewModel.conversation.accounts.count == 1,
let account = viewModel.conversation.accounts.first
{
EmojiTextApp(.init(stringValue: account.safeDisplayName), emojis: account.emojis)
.font(.scaledHeadline)
.foregroundColor(theme.labelColor)
.emojiText.size(Font.scaledHeadlineFont.emojiSize)
.emojiText.baselineOffset(Font.scaledHeadlineFont.emojiBaselineOffset)
} else {
Text("Direct message with \(viewModel.conversation.accounts.count) people")
.font(.scaledHeadline)
}
}
}
.onChange(of: watcher.latestEvent?.id) {
if let latestEvent = watcher.latestEvent {
viewModel.handleEvent(event: latestEvent)
DispatchQueue.main.async {
withAnimation {
scrollProxy?.scrollTo(Constants.bottomAnchor, anchor: .bottom)
.toolbar {
ToolbarItem(placement: .principal) {
if viewModel.conversation.accounts.count == 1,
let account = viewModel.conversation.accounts.first
{
EmojiTextApp(.init(stringValue: account.safeDisplayName), emojis: account.emojis)
.font(.scaledHeadline)
.foregroundColor(theme.labelColor)
.emojiText.size(Font.scaledHeadlineFont.emojiSize)
.emojiText.baselineOffset(Font.scaledHeadlineFont.emojiBaselineOffset)
} else {
Text("Direct message with \(viewModel.conversation.accounts.count) people")
.font(.scaledHeadline)
}
}
}
.onChange(of: watcher.latestEvent?.id) {
if let latestEvent = watcher.latestEvent {
viewModel.handleEvent(event: latestEvent)
DispatchQueue.main.async {
withAnimation {
scrollProxy?.scrollTo(Constants.bottomAnchor, anchor: .bottom)
}
}
}
}
}
}
private var loadingView: some View {

View file

@ -205,12 +205,12 @@ struct ConversationMessageView: View {
.frame(height: 200)
.contentShape(Rectangle())
.onTapGesture {
#if targetEnvironment(macCatalyst) || os(visionOS)
openWindow(value: WindowDestinationMedia.mediaViewer(attachments: [attachement],
selectedAttachment: attachement))
#else
quickLook.prepareFor(selectedMediaAttachment: attachement, mediaAttachments: [attachement])
#endif
#if targetEnvironment(macCatalyst) || os(visionOS)
openWindow(value: WindowDestinationMedia.mediaViewer(attachments: [attachement],
selectedAttachment: attachement))
#else
quickLook.prepareFor(selectedMediaAttachment: attachement, mediaAttachments: [attachement])
#endif
}
}

View file

@ -201,4 +201,3 @@ public struct ThreadsLight: ColorSet {
public init() {}
}

View file

@ -1,11 +1,11 @@
import SwiftUI
public extension View {
@ViewBuilder func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
if condition {
transform(self)
} else {
self
}
@ViewBuilder func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
if condition {
transform(self)
} else {
self
}
}
}

View file

@ -5,11 +5,11 @@ import UIKit
public class SceneDelegate: NSObject, UIWindowSceneDelegate, Sendable {
public var window: UIWindow?
#if os(visionOS)
public private(set) var windowWidth: CGFloat = 0
public private(set) var windowHeight: CGFloat = 0
public private(set) var windowWidth: CGFloat = 0
public private(set) var windowHeight: CGFloat = 0
#else
public private(set) var windowWidth: CGFloat = UIScreen.main.bounds.size.width
public private(set) var windowHeight: CGFloat = UIScreen.main.bounds.size.height
public private(set) var windowWidth: CGFloat = UIScreen.main.bounds.size.width
public private(set) var windowHeight: CGFloat = UIScreen.main.bounds.size.height
#endif
public func scene(_ scene: UIScene,
@ -30,11 +30,11 @@ public class SceneDelegate: NSObject, UIWindowSceneDelegate, Sendable {
override public init() {
super.init()
#if os(visionOS)
windowWidth = window?.bounds.size.width ?? 0
windowHeight = window?.bounds.size.height ?? 0
windowWidth = window?.bounds.size.width ?? 0
windowHeight = window?.bounds.size.height ?? 0
#else
windowWidth = window?.bounds.size.width ?? UIScreen.main.bounds.size.width
windowHeight = window?.bounds.size.height ?? UIScreen.main.bounds.size.height
windowWidth = window?.bounds.size.width ?? UIScreen.main.bounds.size.width
windowHeight = window?.bounds.size.height ?? UIScreen.main.bounds.size.height
#endif
Self.observedSceneDelegate.insert(self)
_ = Self.observer // just for activating the lazy static property
@ -52,25 +52,24 @@ public class SceneDelegate: NSObject, UIWindowSceneDelegate, Sendable {
try? await Task.sleep(for: .seconds(0.1))
for delegate in observedSceneDelegate {
#if os(visionOS)
let newWidth = delegate.window?.bounds.size.width ?? 0
if delegate.windowWidth != newWidth {
delegate.windowWidth = newWidth
}
let newHeight = delegate.window?.bounds.size.height ?? 0
if delegate.windowHeight != newHeight {
delegate.windowHeight = newHeight
}
let newWidth = delegate.window?.bounds.size.width ?? 0
if delegate.windowWidth != newWidth {
delegate.windowWidth = newWidth
}
let newHeight = delegate.window?.bounds.size.height ?? 0
if delegate.windowHeight != newHeight {
delegate.windowHeight = newHeight
}
#else
let newWidth = delegate.window?.bounds.size.width ?? UIScreen.main.bounds.size.width
if delegate.windowWidth != newWidth {
delegate.windowWidth = newWidth
}
let newHeight = delegate.window?.bounds.size.height ?? UIScreen.main.bounds.size.height
if delegate.windowHeight != newHeight {
delegate.windowHeight = newHeight
}
let newWidth = delegate.window?.bounds.size.width ?? UIScreen.main.bounds.size.width
if delegate.windowWidth != newWidth {
delegate.windowWidth = newWidth
}
let newHeight = delegate.window?.bounds.size.height ?? UIScreen.main.bounds.size.height
if delegate.windowHeight != newHeight {
delegate.windowHeight = newHeight
}
#endif
}
}
}

View file

@ -197,7 +197,7 @@ import SwiftUI
// better against the tintColor
private func computeContrastingTintColor() {
func luminance(_ color: Color.Resolved) -> Float {
return 0.299 * color.red + 0.587 * color.green + 0.114 * color.blue;
return 0.299 * color.red + 0.587 * color.green + 0.114 * color.blue
}
let resolvedTintColor = tintColor.resolve(in: .init())
@ -340,7 +340,7 @@ import SwiftUI
ConstellationLight(),
ConstellationDark(),
ThreadsLight(),
ThreadsDark()
ThreadsDark(),
]
}

View file

@ -77,17 +77,15 @@ struct ThemeApplier: ViewModifier {
}
private func setWindowUserInterfaceStyle(_ userInterfaceStyle: UIUserInterfaceStyle) {
allWindows()
.forEach {
$0.overrideUserInterfaceStyle = userInterfaceStyle
}
for window in allWindows() {
window.overrideUserInterfaceStyle = userInterfaceStyle
}
}
private func setWindowTint(_ color: Color) {
allWindows()
.forEach {
$0.tintColor = UIColor(color)
}
for window in allWindows() {
window.tintColor = UIColor(color)
}
}
private func setBarsColor(_ color: Color) {

View file

@ -3,7 +3,7 @@ import SwiftUI
public struct CancelToolbarItem: ToolbarContent {
@Environment(\.dismiss) private var dismiss
public init() { }
public init() {}
public var body: some ToolbarContent {
ToolbarItem(placement: .navigationBarLeading) {

View file

@ -4,7 +4,7 @@ public struct NextPageView: View {
@State private var isLoadingNextPage: Bool = false
@State private var showRetry: Bool = false
let loadNextPage: (() async throws -> Void)
let loadNextPage: () async throws -> Void
public init(loadNextPage: @escaping (() async throws -> Void)) {
self.loadNextPage = loadNextPage

View file

@ -6,65 +6,65 @@ public class HapticManager {
public static let shared: HapticManager = .init()
#if os(visionOS)
public enum FeedbackType: Int {
case success, warning, error
}
public enum FeedbackType: Int {
case success, warning, error
}
#endif
public enum HapticType {
case buttonPress
case dataRefresh(intensity: CGFloat)
#if os(visionOS)
case notification(_ type: FeedbackType)
case notification(_ type: FeedbackType)
#else
case notification(_ type: UINotificationFeedbackGenerator.FeedbackType)
case notification(_ type: UINotificationFeedbackGenerator.FeedbackType)
#endif
case tabSelection
case timeline
}
#if !os(visionOS)
private let selectionGenerator = UISelectionFeedbackGenerator()
private let impactGenerator = UIImpactFeedbackGenerator(style: .heavy)
private let notificationGenerator = UINotificationFeedbackGenerator()
private let selectionGenerator = UISelectionFeedbackGenerator()
private let impactGenerator = UIImpactFeedbackGenerator(style: .heavy)
private let notificationGenerator = UINotificationFeedbackGenerator()
#endif
private let userPreferences = UserPreferences.shared
private init() {
#if !os(visionOS)
selectionGenerator.prepare()
impactGenerator.prepare()
selectionGenerator.prepare()
impactGenerator.prepare()
#endif
}
@MainActor
public func fireHaptic(_ type: HapticType) {
#if !os(visionOS)
guard supportsHaptics else { return }
guard supportsHaptics else { return }
switch type {
case .buttonPress:
if userPreferences.hapticButtonPressEnabled {
impactGenerator.impactOccurred()
switch type {
case .buttonPress:
if userPreferences.hapticButtonPressEnabled {
impactGenerator.impactOccurred()
}
case let .dataRefresh(intensity):
if userPreferences.hapticTimelineEnabled {
impactGenerator.impactOccurred(intensity: intensity)
}
case let .notification(type):
if userPreferences.hapticButtonPressEnabled {
notificationGenerator.notificationOccurred(type)
}
case .tabSelection:
if userPreferences.hapticTabSelectionEnabled {
selectionGenerator.selectionChanged()
}
case .timeline:
if userPreferences.hapticTimelineEnabled {
selectionGenerator.selectionChanged()
}
}
case let .dataRefresh(intensity):
if userPreferences.hapticTimelineEnabled {
impactGenerator.impactOccurred(intensity: intensity)
}
case let .notification(type):
if userPreferences.hapticButtonPressEnabled {
notificationGenerator.notificationOccurred(type)
}
case .tabSelection:
if userPreferences.hapticTabSelectionEnabled {
selectionGenerator.selectionChanged()
}
case .timeline:
if userPreferences.hapticTimelineEnabled {
selectionGenerator.selectionChanged()
}
}
#endif
}

View file

@ -64,7 +64,7 @@ public enum SheetDestination: Identifiable {
public var id: String {
switch self {
case .editStatusEditor, .newStatusEditor, .replyToStatusEditor, .quoteStatusEditor,
.mentionStatusEditor, .quoteLinkStatusEditor:
.mentionStatusEditor, .quoteLinkStatusEditor:
"statusEditor"
case .listCreate:
"listCreate"
@ -147,8 +147,9 @@ public enum SheetDestination: Identifiable {
navigate(to: .hashTag(tag: tag, account: nil))
return .handled
} else if url.lastPathComponent.first == "@",
let host = url.host,
!host.hasPrefix("www") {
let host = url.host,
!host.hasPrefix("www")
{
let acct = "\(url.lastPathComponent)@\(host)"
Task {
await navigateToAccountFrom(acct: acct, url: url)

View file

@ -65,7 +65,7 @@ import OSLog
connect()
}
watchedStreams = streams
streams.forEach { stream in
for stream in streams {
sendMessage(message: StreamMessage(type: "subscribe", stream: stream.rawValue))
}
}
@ -159,19 +159,19 @@ import OSLog
public func emmitDeleteEvent(for status: String) {
let event = StreamEventDelete(status: status)
self.events.append(event)
self.latestEvent = event
events.append(event)
latestEvent = event
}
public func emmitEditEvent(for status: Status) {
let event = StreamEventStatusUpdate(status: status)
self.events.append(event)
self.latestEvent = event
events.append(event)
latestEvent = event
}
public func emmitPostEvent(for status: Status) {
let event = StreamEventUpdate(status: status)
self.events.append(event)
self.latestEvent = event
events.append(event)
latestEvent = event
}
}

View file

@ -183,7 +183,6 @@ import SwiftUI
}
}
public var alwaysUseDeepl: Bool {
didSet {
storage.alwaysUseDeepl = alwaysUseDeepl
@ -415,7 +414,7 @@ import SwiftUI
}
public var totalNotificationsCount: Int {
notificationsCount.compactMap{ $0.value }.reduce(0, +)
notificationsCount.compactMap { $0.value }.reduce(0, +)
}
public func reloadNotificationsCount(tokens: [OauthToken]) {

View file

@ -1,7 +1,7 @@
@testable import Env
import XCTest
import SwiftUI
import Network
import SwiftUI
import XCTest
@MainActor
final class RouterTests: XCTestCase {

View file

@ -56,9 +56,9 @@ public struct ExploreView: View {
EmptyView(iconName: "magnifyingglass",
title: "explore.search.title",
message: "explore.search.message-\(client.server)")
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.secondaryBackgroundColor)
#endif
#endif
.listRowSeparator(.hidden)
} else {
quickAccessView
@ -94,32 +94,32 @@ public struct ExploreView: View {
}
.listStyle(.plain)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
#endif
.navigationTitle("explore.navigation-title")
.navigationBarTitleDisplayMode(.inline)
.searchable(text: $viewModel.searchQuery,
isPresented: $viewModel.isSearchPresented,
placement: .navigationBarDrawer(displayMode: .always),
prompt: Text("explore.search.prompt"))
.searchScopes($viewModel.searchScope) {
ForEach(ExploreViewModel.SearchScope.allCases, id: \.self) { scope in
Text(scope.localizedString)
}
}
.task(id: viewModel.searchQuery) {
await viewModel.search()
}
.onChange(of: scrollToTopSignal) {
if viewModel.scrollToTopVisible {
viewModel.isSearchPresented.toggle()
} else {
withAnimation {
proxy.scrollTo(ScrollToView.Constants.scrollToTop, anchor: .top)
.navigationTitle("explore.navigation-title")
.navigationBarTitleDisplayMode(.inline)
.searchable(text: $viewModel.searchQuery,
isPresented: $viewModel.isSearchPresented,
placement: .navigationBarDrawer(displayMode: .always),
prompt: Text("explore.search.prompt"))
.searchScopes($viewModel.searchScope) {
ForEach(ExploreViewModel.SearchScope.allCases, id: \.self) { scope in
Text(scope.localizedString)
}
}
.task(id: viewModel.searchQuery) {
await viewModel.search()
}
.onChange(of: scrollToTopSignal) {
if viewModel.scrollToTopVisible {
viewModel.isSearchPresented.toggle()
} else {
withAnimation {
proxy.scrollTo(ScrollToView.Constants.scrollToTop, anchor: .top)
}
}
}
}
}
}
@ -148,9 +148,9 @@ public struct ExploreView: View {
.scrollIndicators(.never)
.listRowInsets(EdgeInsets())
#if !os(visionOS)
.listRowBackground(theme.secondaryBackgroundColor)
.listRowBackground(theme.secondaryBackgroundColor)
#endif
.listRowSeparator(.hidden)
.listRowSeparator(.hidden)
}
private var loadingView: some View {
@ -159,9 +159,9 @@ public struct ExploreView: View {
.padding(.vertical, 8)
.redacted(reason: .placeholder)
.allowsHitTesting(false)
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#endif
#endif
}
}
@ -172,13 +172,13 @@ public struct ExploreView: View {
ForEach(results.accounts) { account in
if let relationship = results.relationships.first(where: { $0.id == account.id }) {
AccountsListRow(viewModel: .init(account: account, relationShip: relationship))
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#else
#else
.listRowBackground(RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.background).hoverEffect())
.foregroundStyle(.background).hoverEffect())
.listRowHoverEffectDisabled()
#endif
#endif
}
}
}
@ -187,13 +187,13 @@ public struct ExploreView: View {
Section("explore.section.tags") {
ForEach(results.hashtags) { tag in
TagRowView(tag: tag)
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#else
#else
.listRowBackground(RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.background).hoverEffect())
.foregroundStyle(.background).hoverEffect())
.listRowHoverEffectDisabled()
#endif
#endif
.padding(.vertical, 4)
}
}
@ -202,13 +202,13 @@ public struct ExploreView: View {
Section("explore.section.posts") {
ForEach(results.statuses) { status in
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#else
#else
.listRowBackground(RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.background).hoverEffect())
.foregroundStyle(.background).hoverEffect())
.listRowHoverEffectDisabled()
#endif
#endif
.padding(.vertical, 8)
}
}
@ -222,13 +222,13 @@ public struct ExploreView: View {
{ account in
if let relationship = viewModel.suggestedAccountsRelationShips.first(where: { $0.id == account.id }) {
AccountsListRow(viewModel: .init(account: account, relationShip: relationship))
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#else
#else
.listRowBackground(RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.background).hoverEffect())
.foregroundStyle(.background).hoverEffect())
.listRowHoverEffectDisabled()
#endif
#endif
}
}
NavigationLink(value: RouterDestination.accountsList(accounts: viewModel.suggestedAccounts)) {
@ -239,7 +239,7 @@ public struct ExploreView: View {
.listRowBackground(theme.primaryBackgroundColor)
#else
.listRowBackground(RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.background).hoverEffect())
.foregroundStyle(.background).hoverEffect())
.listRowHoverEffectDisabled()
#endif
}
@ -251,13 +251,13 @@ public struct ExploreView: View {
.prefix(upTo: viewModel.trendingTags.count > 5 ? 5 : viewModel.trendingTags.count))
{ tag in
TagRowView(tag: tag)
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#else
#else
.listRowBackground(RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.background).hoverEffect())
.foregroundStyle(.background).hoverEffect())
.listRowHoverEffectDisabled()
#endif
#endif
.padding(.vertical, 4)
}
NavigationLink(value: RouterDestination.tagsList(tags: viewModel.trendingTags)) {
@ -268,7 +268,7 @@ public struct ExploreView: View {
.listRowBackground(theme.primaryBackgroundColor)
#else
.listRowBackground(RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.background).hoverEffect())
.foregroundStyle(.background).hoverEffect())
.listRowHoverEffectDisabled()
#endif
}
@ -280,13 +280,13 @@ public struct ExploreView: View {
.prefix(upTo: viewModel.trendingStatuses.count > 3 ? 3 : viewModel.trendingStatuses.count))
{ status in
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#else
#else
.listRowBackground(RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.background).hoverEffect())
.foregroundStyle(.background).hoverEffect())
.listRowHoverEffectDisabled()
#endif
#endif
.padding(.vertical, 8)
}
@ -298,7 +298,7 @@ public struct ExploreView: View {
.listRowBackground(theme.primaryBackgroundColor)
#else
.listRowBackground(RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.background).hoverEffect())
.foregroundStyle(.background).hoverEffect())
.listRowHoverEffectDisabled()
#endif
}
@ -311,13 +311,13 @@ public struct ExploreView: View {
{ card in
StatusRowCardView(card: card)
.environment(\.isCompact, true)
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#else
#else
.listRowBackground(RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.background).hoverEffect())
.foregroundStyle(.background).hoverEffect())
.listRowHoverEffectDisabled()
#endif
#endif
.padding(.vertical, 8)
}
@ -329,7 +329,7 @@ public struct ExploreView: View {
.listRowBackground(theme.primaryBackgroundColor)
#else
.listRowBackground(RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.background).hoverEffect())
.foregroundStyle(.background).hoverEffect())
.listRowHoverEffectDisabled()
#endif
}

View file

@ -15,9 +15,9 @@ public struct TagsListView: View {
List {
ForEach(tags) { tag in
TagRowView(tag: tag)
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#endif
#endif
.padding(.vertical, 4)
}
}

View file

@ -1,8 +1,8 @@
import DesignSystem
import Models
import Network
import StatusKit
import SwiftUI
import Network
public struct TrendingLinksListView: View {
@Environment(Theme.self) private var theme
@ -19,9 +19,9 @@ public struct TrendingLinksListView: View {
ForEach(links) { card in
StatusRowCardView(card: card)
.environment(\.isCompact, true)
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#endif
#endif
.padding(.vertical, 8)
}
NextPageView {

View file

@ -1,5 +1,5 @@
import SwiftUI
import Models
import SwiftUI
enum DisplayType {
case image

View file

@ -1,9 +1,9 @@
import AVKit
import DesignSystem
import Env
import Models
import Observation
import SwiftUI
import Models
@MainActor
@Observable public class MediaUIAttachmentVideoViewModel {
@ -21,9 +21,9 @@ import Models
player = .init(url: url)
player?.audiovisualBackgroundPlaybackPolicy = .pauses
#if !os(visionOS)
player?.preventsDisplaySleepDuringVideoPlayback = false
player?.preventsDisplaySleepDuringVideoPlayback = false
#endif
if (autoPlay || forceAutoPlay) && !isCompact {
if autoPlay || forceAutoPlay, !isCompact {
player?.play()
isPlaying = true
} else {
@ -70,7 +70,7 @@ import Models
func preventSleep(_ preventSleep: Bool) {
#if !os(visionOS)
player?.preventsDisplaySleepDuringVideoPlayback = preventSleep
player?.preventsDisplaySleepDuringVideoPlayback = preventSleep
#endif
}
@ -96,90 +96,91 @@ public struct MediaUIAttachmentVideoView: View {
public var body: some View {
videoView
.onAppear {
viewModel.preparePlayer(autoPlay: isFullScreen ? true : preferences.autoPlayVideo,
isCompact: isCompact)
viewModel.mute(preferences.muteVideo)
}
.onDisappear {
viewModel.stop()
}
.onTapGesture {
if !preferences.autoPlayVideo && !viewModel.isPlaying {
viewModel.play()
return
}
#if targetEnvironment(macCatalyst)
viewModel.pause()
let attachement = MediaAttachment.videoWith(url: viewModel.url)
openWindow(value: WindowDestinationMedia.mediaViewer(attachments: [attachement], selectedAttachment: attachement))
#else
isFullScreen = true
#endif
}
.fullScreenCover(isPresented: $isFullScreen) {
NavigationStack {
videoView
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button { isFullScreen.toggle() } label: {
Image(systemName: "xmark.circle")
}
}
QuickLookToolbarItem(itemUrl: viewModel.url)
}
}
.onAppear {
DispatchQueue.global().async {
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
try? AVAudioSession.sharedInstance().setCategory(.playback, options: .duckOthers)
try? AVAudioSession.sharedInstance().setActive(true)
}
viewModel.preventSleep(true)
viewModel.mute(false)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
if isCompact || !preferences.autoPlayVideo {
viewModel.play()
} else {
viewModel.resume()
}
}
viewModel.preparePlayer(autoPlay: isFullScreen ? true : preferences.autoPlayVideo,
isCompact: isCompact)
viewModel.mute(preferences.muteVideo)
}
.onDisappear {
if isCompact || !preferences.autoPlayVideo {
viewModel.pause()
}
viewModel.preventSleep(false)
viewModel.mute(preferences.muteVideo)
DispatchQueue.global().async {
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
try? AVAudioSession.sharedInstance().setCategory(.ambient, options: .mixWithOthers)
try? AVAudioSession.sharedInstance().setActive(true)
}
viewModel.stop()
}
}
.cornerRadius(4)
.onChange(of: scenePhase) { _, newValue in
switch newValue {
case .background, .inactive:
viewModel.pause()
case .active:
if (preferences.autoPlayVideo || viewModel.forceAutoPlay || isFullScreen) && !isCompact {
.onTapGesture {
if !preferences.autoPlayVideo && !viewModel.isPlaying {
viewModel.play()
return
}
#if targetEnvironment(macCatalyst)
viewModel.pause()
let attachement = MediaAttachment.videoWith(url: viewModel.url)
openWindow(value: WindowDestinationMedia.mediaViewer(attachments: [attachement], selectedAttachment: attachement))
#else
isFullScreen = true
#endif
}
.fullScreenCover(isPresented: $isFullScreen) {
NavigationStack {
videoView
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button { isFullScreen.toggle() } label: {
Image(systemName: "xmark.circle")
}
}
QuickLookToolbarItem(itemUrl: viewModel.url)
}
}
.onAppear {
DispatchQueue.global().async {
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
try? AVAudioSession.sharedInstance().setCategory(.playback, options: .duckOthers)
try? AVAudioSession.sharedInstance().setActive(true)
}
viewModel.preventSleep(true)
viewModel.mute(false)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
if isCompact || !preferences.autoPlayVideo {
viewModel.play()
} else {
viewModel.resume()
}
}
}
.onDisappear {
if isCompact || !preferences.autoPlayVideo {
viewModel.pause()
}
viewModel.preventSleep(false)
viewModel.mute(preferences.muteVideo)
DispatchQueue.global().async {
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
try? AVAudioSession.sharedInstance().setCategory(.ambient, options: .mixWithOthers)
try? AVAudioSession.sharedInstance().setActive(true)
}
}
}
.cornerRadius(4)
.onChange(of: scenePhase) { _, newValue in
switch newValue {
case .background, .inactive:
viewModel.pause()
case .active:
if (preferences.autoPlayVideo || viewModel.forceAutoPlay || isFullScreen) && !isCompact {
viewModel.play()
}
default:
break
}
default:
break
}
}
}
private var videoView: some View {
VideoPlayer(player: viewModel.player, videoOverlay: {
if !preferences.autoPlayVideo,
!viewModel.forceAutoPlay,
!viewModel.forceAutoPlay,
!isFullScreen,
!viewModel.isPlaying,
!isCompact {
!viewModel.isPlaying,
!isCompact
{
Button(action: {
viewModel.play()
}, label: {

View file

@ -1,8 +1,8 @@
import AVFoundation
import Models
import Nuke
import QuickLook
import SwiftUI
import AVFoundation
public struct MediaUIView: View, @unchecked Sendable {
private let data: [DisplayData]

View file

@ -1,6 +1,6 @@
import SwiftUI
import NukeUI
import Nuke
import NukeUI
import SwiftUI
struct QuickLookToolbarItem: ToolbarContent, @unchecked Sendable {
let itemUrl: URL

View file

@ -92,9 +92,9 @@ public final class Account: Codable, Identifiable, Hashable, Sendable, Equatable
self.discoverable = discoverable
if let displayName, !displayName.isEmpty {
self.cachedDisplayName = .init(stringValue: displayName)
cachedDisplayName = .init(stringValue: displayName)
} else {
self.cachedDisplayName = .init(stringValue: "@\(username)")
cachedDisplayName = .init(stringValue: "@\(username)")
}
}
@ -122,29 +122,29 @@ public final class Account: Codable, Identifiable, Hashable, Sendable, Equatable
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(String.self, forKey: .id)
self.username = try container.decode(String.self, forKey: .username)
self.displayName = try container.decodeIfPresent(String.self, forKey: .displayName)
self.avatar = try container.decode(URL.self, forKey: .avatar)
self.header = try container.decode(URL.self, forKey: .header)
self.acct = try container.decode(String.self, forKey: .acct)
self.note = try container.decode(HTMLString.self, forKey: .note)
self.createdAt = try container.decode(ServerDate.self, forKey: .createdAt)
self.followersCount = try container.decodeIfPresent(Int.self, forKey: .followersCount)
self.followingCount = try container.decodeIfPresent(Int.self, forKey: .followingCount)
self.statusesCount = try container.decodeIfPresent(Int.self, forKey: .statusesCount)
self.lastStatusAt = try container.decodeIfPresent(String.self, forKey: .lastStatusAt)
self.fields = try container.decode([Account.Field].self, forKey: .fields)
self.locked = try container.decode(Bool.self, forKey: .locked)
self.emojis = try container.decode([Emoji].self, forKey: .emojis)
self.url = try container.decodeIfPresent(URL.self, forKey: .url)
self.source = try container.decodeIfPresent(Account.Source.self, forKey: .source)
self.bot = try container.decode(Bool.self, forKey: .bot)
self.discoverable = try container.decodeIfPresent(Bool.self, forKey: .discoverable)
id = try container.decode(String.self, forKey: .id)
username = try container.decode(String.self, forKey: .username)
displayName = try container.decodeIfPresent(String.self, forKey: .displayName)
avatar = try container.decode(URL.self, forKey: .avatar)
header = try container.decode(URL.self, forKey: .header)
acct = try container.decode(String.self, forKey: .acct)
note = try container.decode(HTMLString.self, forKey: .note)
createdAt = try container.decode(ServerDate.self, forKey: .createdAt)
followersCount = try container.decodeIfPresent(Int.self, forKey: .followersCount)
followingCount = try container.decodeIfPresent(Int.self, forKey: .followingCount)
statusesCount = try container.decodeIfPresent(Int.self, forKey: .statusesCount)
lastStatusAt = try container.decodeIfPresent(String.self, forKey: .lastStatusAt)
fields = try container.decode([Account.Field].self, forKey: .fields)
locked = try container.decode(Bool.self, forKey: .locked)
emojis = try container.decode([Emoji].self, forKey: .emojis)
url = try container.decodeIfPresent(URL.self, forKey: .url)
source = try container.decodeIfPresent(Account.Source.self, forKey: .source)
bot = try container.decode(Bool.self, forKey: .bot)
discoverable = try container.decodeIfPresent(Bool.self, forKey: .discoverable)
if let displayName, !displayName.isEmpty {
self.cachedDisplayName = .init(stringValue: displayName)
cachedDisplayName = .init(stringValue: displayName)
} else {
self.cachedDisplayName = .init(stringValue: "@\(username)")
cachedDisplayName = .init(stringValue: "@\(username)")
}
}

View file

@ -15,7 +15,7 @@ public struct ServerDate: Codable, Hashable, Equatable, Sendable {
relativeTo: Date())
} else {
return Duration.seconds(-date.timeIntervalSinceNow).formatted(.units(width: .narrow,
maximumUnitCount: 1))
maximumUnitCount: 1))
}
}

View file

@ -39,7 +39,7 @@ public struct ConsolidatedNotification: Identifiable {
public static func placeholders() -> [ConsolidatedNotification] {
[.placeholder(), .placeholder(), .placeholder(),
.placeholder(), .placeholder(), .placeholder(),
.placeholder(), .placeholder(), .placeholder(),
.placeholder(), .placeholder(), .placeholder(),
.placeholder(), .placeholder(), .placeholder()]
}

View file

@ -1,15 +1,15 @@
import Foundation
public enum PostError: Error {
// Throw when any attached media is missing media description (alt text)
case missingAltText
// Throw when any attached media is missing media description (alt text)
case missingAltText
}
extension PostError: CustomStringConvertible {
public var description: String {
switch self {
case .missingAltText:
return NSLocalizedString("status.error.no-alt-text", comment: "media does not have media description")
}
public var description: String {
switch self {
case .missingAltText:
return NSLocalizedString("status.error.no-alt-text", comment: "media does not have media description")
}
}
}

View file

@ -22,7 +22,7 @@ public struct StreamEventUpdate: StreamEvent {
public struct StreamEventStatusUpdate: StreamEvent {
public let date = Date()
public var id: String { status.id + (status.editedAt?.asDate.description ?? "")}
public var id: String { status.id + (status.editedAt?.asDate.description ?? "") }
public let status: Status
public init(status: Status) {
self.status = status

View file

@ -7,7 +7,7 @@ public struct Tag: Codable, Identifiable, Equatable, Hashable {
public static func == (lhs: Tag, rhs: Tag) -> Bool {
lhs.name == rhs.name &&
lhs.following == rhs.following
lhs.following == rhs.following
}
public var id: String {

View file

@ -3,8 +3,8 @@ import Foundation
import Models
import Observation
import os
import SwiftUI
import OSLog
import SwiftUI
@Observable public final class Client: Equatable, Identifiable, Hashable, @unchecked Sendable {
public static func == (lhs: Client, rhs: Client) -> Bool {
@ -286,7 +286,8 @@ import OSLog
method: String,
mimeType: String,
filename: String,
data: Data) throws -> URLRequest {
data: Data) throws -> URLRequest
{
let url = try makeURL(endpoint: endpoint, forceVersion: version)
var request = makeURLRequest(url: url, endpoint: endpoint, httpMethod: method)
let boundary = UUID().uuidString

View file

@ -34,8 +34,8 @@ public struct InstanceSocialClient: Sendable {
}
}
extension Array where Self.Element == InstanceSocial {
fileprivate func sorted(by keyword: String) -> Self {
private extension Array where Self.Element == InstanceSocial {
func sorted(by keyword: String) -> Self {
let keyword = keyword.trimmingCharacters(in: .whitespacesAndNewlines)
var newArray = self

View file

@ -160,7 +160,7 @@ struct NotificationRowView: View {
client: client,
routerPath: routerPath,
showActions: true))
.environment(\.isMediaCompact, false)
.environment(\.isMediaCompact, false)
} else {
StatusRowView(viewModel: .init(status: status,
client: client,

View file

@ -88,44 +88,44 @@ public struct NotificationsListView: View {
}
.navigationBarTitleDisplayMode(.inline)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor)
#endif
.onAppear {
viewModel.client = client
viewModel.currentAccount = account
if let lockedType {
viewModel.isLockedType = true
viewModel.selectedType = lockedType
} else {
viewModel.loadSelectedType()
}
Task {
await viewModel.fetchNotifications()
}
}
.refreshable {
SoundEffectManager.shared.playSound(.pull)
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.3))
await viewModel.fetchNotifications()
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.7))
SoundEffectManager.shared.playSound(.refresh)
}
.onChange(of: watcher.latestEvent?.id) {
if let latestEvent = watcher.latestEvent {
viewModel.handleEvent(event: latestEvent)
}
}
.onChange(of: scenePhase) { _, newValue in
switch newValue {
case .active:
.onAppear {
viewModel.client = client
viewModel.currentAccount = account
if let lockedType {
viewModel.isLockedType = true
viewModel.selectedType = lockedType
} else {
viewModel.loadSelectedType()
}
Task {
await viewModel.fetchNotifications()
}
default:
break
}
}
.refreshable {
SoundEffectManager.shared.playSound(.pull)
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.3))
await viewModel.fetchNotifications()
HapticManager.shared.fireHaptic(.dataRefresh(intensity: 0.7))
SoundEffectManager.shared.playSound(.refresh)
}
.onChange(of: watcher.latestEvent?.id) {
if let latestEvent = watcher.latestEvent {
viewModel.handleEvent(event: latestEvent)
}
}
.onChange(of: scenePhase) { _, newValue in
switch newValue {
case .active:
Task {
await viewModel.fetchNotifications()
}
default:
break
}
}
}
@ViewBuilder
@ -141,14 +141,14 @@ public struct NotificationsListView: View {
leading: .layoutPadding + 4,
bottom: 0,
trailing: .layoutPadding))
#if os(visionOS)
#if os(visionOS)
.listRowBackground(RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.background))
#else
.listRowBackground(theme.primaryBackgroundColor)
#endif
.redacted(reason: .placeholder)
.allowsHitTesting(false)
#else
.listRowBackground(theme.primaryBackgroundColor)
#endif
.redacted(reason: .placeholder)
.allowsHitTesting(false)
}
case let .display(notifications, nextPageState):
@ -156,9 +156,9 @@ public struct NotificationsListView: View {
EmptyView(iconName: "bell.slash",
title: "notifications.empty.title",
message: "notifications.empty.message")
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#endif
#endif
.listSectionSeparator(.hidden)
} else {
ForEach(notifications) { notification in
@ -170,14 +170,14 @@ public struct NotificationsListView: View {
leading: .layoutPadding + 4,
bottom: 6,
trailing: .layoutPadding))
#if os(visionOS)
#if os(visionOS)
.listRowBackground(RoundedRectangle(cornerRadius: 8)
.foregroundStyle(notification.type == .mention && lockedType != .mention ? Material.thick : Material.regular).hoverEffect())
.listRowHoverEffectDisabled()
#else
#else
.listRowBackground(notification.type == .mention && lockedType != .mention ?
theme.secondaryBackgroundColor : theme.primaryBackgroundColor)
#endif
#endif
.id(notification.id)
}
@ -193,7 +193,7 @@ public struct NotificationsListView: View {
bottom: .layoutPadding,
trailing: .layoutPadding))
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
.listRowBackground(theme.primaryBackgroundColor)
#endif
}
}

View file

@ -164,7 +164,7 @@ import SwiftUI
Task {
do {
let _: Marker = try await client.post(endpoint: Markers.markNotifications(lastReadId: id))
} catch { }
} catch {}
}
}

View file

@ -54,15 +54,15 @@ public struct StatusDetailView: View {
loadingContextView
}
#if !os(visionOS)
Rectangle()
.foregroundColor(theme.secondaryBackgroundColor)
.frame(minHeight: reader.frame(in: .local).size.height - statusHeight)
.listRowSeparator(.hidden)
.listRowBackground(theme.secondaryBackgroundColor)
.listRowInsets(.init())
.accessibilityHidden(true)
#endif
#if !os(visionOS)
Rectangle()
.foregroundColor(theme.secondaryBackgroundColor)
.frame(minHeight: reader.frame(in: .local).size.height - statusHeight)
.listRowSeparator(.hidden)
.listRowBackground(theme.secondaryBackgroundColor)
.listRowInsets(.init())
.accessibilityHidden(true)
#endif
case .error:
errorView
@ -71,33 +71,33 @@ public struct StatusDetailView: View {
.environment(\.defaultMinListRowHeight, 1)
.listStyle(.plain)
#if !os(visionOS)
.scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor)
.scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor)
#endif
.onChange(of: viewModel.scrollToId) { _, newValue in
if let newValue {
viewModel.scrollToId = nil
proxy.scrollTo(newValue, anchor: .top)
.onChange(of: viewModel.scrollToId) { _, newValue in
if let newValue {
viewModel.scrollToId = nil
proxy.scrollTo(newValue, anchor: .top)
}
}
}
.onAppear {
guard !isLoaded else { return }
viewModel.client = client
viewModel.routerPath = routerPath
Task {
let result = await viewModel.fetch()
isLoaded = true
.onAppear {
guard !isLoaded else { return }
viewModel.client = client
viewModel.routerPath = routerPath
Task {
let result = await viewModel.fetch()
isLoaded = true
if !result {
if let url = viewModel.remoteStatusURL {
await UIApplication.shared.open(url)
}
DispatchQueue.main.async {
_ = routerPath.path.popLast()
if !result {
if let url = viewModel.remoteStatusURL {
await UIApplication.shared.open(url)
}
DispatchQueue.main.async {
_ = routerPath.path.popLast()
}
}
}
}
}
}
.refreshable {
Task {
@ -137,9 +137,9 @@ public struct StatusDetailView: View {
}
}
}
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(viewModel.highlightRowColor)
#endif
#endif
.listRowInsets(.init(top: 12,
leading: .layoutPadding,
bottom: 12,
@ -179,16 +179,16 @@ public struct StatusDetailView: View {
.frame(height: 50)
.listRowSeparator(.hidden)
#if !os(visionOS)
.listRowBackground(theme.secondaryBackgroundColor)
.listRowBackground(theme.secondaryBackgroundColor)
#endif
.listRowInsets(.init())
.listRowInsets(.init())
}
private var topPaddingView: some View {
HStack { EmptyView() }
#if !os(visionOS)
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#endif
#endif
.listRowSeparator(.hidden)
.listRowInsets(.init())
.frame(height: .layoutPadding)

View file

@ -37,5 +37,4 @@ extension StatusEditor {
}
}
}
}

View file

@ -1,7 +1,7 @@
import DesignSystem
import Env
#if !os(visionOS) && !DEBUG
import GiphyUISDK
import GiphyUISDK
#endif
import Models
import NukeUI
@ -29,47 +29,47 @@ extension StatusEditor {
var body: some View {
@Bindable var viewModel = focusedSEVM
#if os(visionOS)
HStack {
contentView
.buttonStyle(.borderless)
}
.frame(width: 32)
.padding(16)
.glassBackgroundEffect()
.cornerRadius(8)
.padding(.trailing, 78)
HStack {
contentView
.buttonStyle(.borderless)
}
.frame(width: 32)
.padding(16)
.glassBackgroundEffect()
.cornerRadius(8)
.padding(.trailing, 78)
#else
Divider()
HStack {
contentView
}
.frame(height: 20)
.padding(.vertical, 12)
.background(.ultraThickMaterial)
Divider()
HStack {
contentView
}
.frame(height: 20)
.padding(.vertical, 12)
.background(.ultraThickMaterial)
#endif
}
@ViewBuilder
private var contentView: some View {
#if os(visionOS)
VStack(spacing: 8) {
actionsView
}
#else
ViewThatFits {
HStack(alignment: .center, spacing: 16) {
VStack(spacing: 8) {
actionsView
}
.padding(.horizontal, .layoutPadding)
ScrollView(.horizontal) {
#else
ViewThatFits {
HStack(alignment: .center, spacing: 16) {
actionsView
}
.padding(.horizontal, .layoutPadding)
ScrollView(.horizontal) {
HStack(alignment: .center, spacing: 16) {
actionsView
}
.padding(.horizontal, .layoutPadding)
}
.scrollIndicators(.hidden)
}
.scrollIndicators(.hidden)
}
#endif
}
@ -96,11 +96,11 @@ extension StatusEditor {
}
#if !os(visionOS)
Button {
isGIFPickerPresented = true
} label: {
Label("GIPHY", systemImage: "party.popper")
}
Button {
isGIFPickerPresented = true
} label: {
Label("GIPHY", systemImage: "party.popper")
}
#endif
} label: {
if viewModel.isMediasLoading {
@ -135,31 +135,30 @@ extension StatusEditor {
.sheet(isPresented: $isGIFPickerPresented, content: {
#if !os(visionOS) && !DEBUG
#if targetEnvironment(macCatalyst)
NavigationStack {
giphyView
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button {
isGIFPickerPresented = false
} label: {
Image(systemName: "xmark.circle")
NavigationStack {
giphyView
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button {
isGIFPickerPresented = false
} label: {
Image(systemName: "xmark.circle")
}
}
}
}
}
}
.presentationDetents([.medium, .large])
#else
giphyView
.presentationDetents([.medium, .large])
#else
giphyView
.presentationDetents([.medium, .large])
#endif
#else
EmptyView()
EmptyView()
#endif
})
.accessibilityLabel("accessibility.editor.button.attach-photo")
.disabled(viewModel.showPoll)
Button {
// all SEVM have the same visibility value
followUpSEVMs.append(ViewModel(mode: .new(visibility: focusedSEVM.visibility)))
@ -188,7 +187,6 @@ extension StatusEditor {
}
}
if preferences.isOpenAIEnabled {
AIMenu.disabled(!viewModel.canPost)
}
@ -223,19 +221,19 @@ extension StatusEditor {
}
#if !os(visionOS) && !DEBUG
@ViewBuilder
private var giphyView: some View {
@Bindable var viewModel = focusedSEVM
GifPickerView { url in
GPHCache.shared.downloadAssetData(url) { data, _ in
guard let data else { return }
viewModel.processGIFData(data: data)
@ViewBuilder
private var giphyView: some View {
@Bindable var viewModel = focusedSEVM
GifPickerView { url in
GPHCache.shared.downloadAssetData(url) { data, _ in
guard let data else { return }
viewModel.processGIFData(data: data)
}
isGIFPickerPresented = false
} onShouldDismissGifPicker: {
isGIFPickerPresented = false
}
isGIFPickerPresented = false
} onShouldDismissGifPicker: {
isGIFPickerPresented = false
}
}
#endif
private var AIMenu: some View {
@ -268,7 +266,5 @@ extension StatusEditor {
}
}
}
}
}

View file

@ -1,12 +1,11 @@
import DesignSystem
import EmojiText
import Foundation
import SwiftUI
import Models
import SwiftData
import SwiftUI
extension StatusEditor {
@MainActor
struct AutoCompleteView: View {
@Environment(\.modelContext) var context
@ -21,8 +20,9 @@ extension StatusEditor {
var body: some View {
if !viewModel.mentionsSuggestions.isEmpty ||
!viewModel.tagsSuggestions.isEmpty ||
(viewModel.showRecentsTagsInline && !recentTags.isEmpty) {
!viewModel.tagsSuggestions.isEmpty ||
(viewModel.showRecentsTagsInline && !recentTags.isEmpty)
{
VStack {
HStack {
ScrollView(.horizontal, showsIndicators: false) {

View file

@ -1,10 +1,10 @@
import DesignSystem
import EmojiText
import Env
import Foundation
import SwiftUI
import Models
import SwiftData
import Env
import SwiftUI
extension StatusEditor.AutoCompleteView {
@MainActor

View file

@ -1,12 +1,11 @@
import DesignSystem
import EmojiText
import Foundation
import SwiftUI
import Models
import SwiftData
import SwiftUI
extension StatusEditor.AutoCompleteView {
extension StatusEditor.AutoCompleteView {
struct MentionsView: View {
@Environment(Theme.self) private var theme

View file

@ -1,12 +1,11 @@
import DesignSystem
import EmojiText
import Foundation
import SwiftUI
import Models
import SwiftData
import SwiftUI
extension StatusEditor.AutoCompleteView {
extension StatusEditor.AutoCompleteView {
struct RecentTagsView: View {
@Environment(Theme.self) private var theme

View file

@ -1,10 +1,9 @@
import DesignSystem
import EmojiText
import Foundation
import SwiftUI
import Models
import SwiftData
import SwiftUI
extension StatusEditor.AutoCompleteView {
struct RemoteTagsView: View {

View file

@ -23,7 +23,7 @@ extension StatusEditor {
func makeUIViewController(context: Context) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
#if !os(visionOS)
imagePicker.sourceType = .camera
imagePicker.sourceType = .camera
#endif
imagePicker.delegate = context.coordinator
return imagePicker
@ -35,5 +35,4 @@ extension StatusEditor {
Coordinator(picker: self)
}
}
}

View file

@ -7,5 +7,4 @@ extension StatusEditor {
let categoryName: String
var emojis: [Emoji]
}
}

View file

@ -2,9 +2,9 @@ import AVFoundation
import Foundation
import UIKit
extension StatusEditor {
public actor Compressor {
public init() { }
public extension StatusEditor {
actor Compressor {
public init() {}
enum CompressorError: Error {
case noData
@ -106,5 +106,4 @@ extension StatusEditor {
}
}
}
}

View file

@ -1,11 +1,10 @@
import DesignSystem
import Env
import SwiftUI
import Models
import NukeUI
import SwiftUI
extension StatusEditor {
@MainActor
struct CustomEmojisView: View {
@Environment(\.dismiss) private var dismiss

View file

@ -1,53 +1,53 @@
#if !os(visionOS) && !DEBUG
import DesignSystem
import GiphyUISDK
import SwiftUI
import UIKit
import DesignSystem
import GiphyUISDK
import SwiftUI
import UIKit
struct GifPickerView: UIViewControllerRepresentable {
@Environment(Theme.self) private var theme
struct GifPickerView: UIViewControllerRepresentable {
@Environment(Theme.self) private var theme
var completion: (String) -> Void
var onShouldDismissGifPicker: () -> Void
var completion: (String) -> Void
var onShouldDismissGifPicker: () -> Void
func makeUIViewController(context: Context) -> GiphyViewController {
Giphy.configure(apiKey: "MIylJkNX57vcUNZxmSODKU9dQKBgXCkV")
func makeUIViewController(context: Context) -> GiphyViewController {
Giphy.configure(apiKey: "MIylJkNX57vcUNZxmSODKU9dQKBgXCkV")
let controller = GiphyViewController()
controller.swiftUIEnabled = true
controller.mediaTypeConfig = [.gifs, .stickers, .recents]
controller.delegate = context.coordinator
controller.navigationController?.isNavigationBarHidden = true
controller.navigationController?.setNavigationBarHidden(true, animated: false)
let controller = GiphyViewController()
controller.swiftUIEnabled = true
controller.mediaTypeConfig = [.gifs, .stickers, .recents]
controller.delegate = context.coordinator
controller.navigationController?.isNavigationBarHidden = true
controller.navigationController?.setNavigationBarHidden(true, animated: false)
GiphyViewController.trayHeightMultiplier = 1.0
GiphyViewController.trayHeightMultiplier = 1.0
controller.theme = GPHTheme(type: theme.selectedScheme == .dark ? .darkBlur : .lightBlur)
controller.theme = GPHTheme(type: theme.selectedScheme == .dark ? .darkBlur : .lightBlur)
return controller
}
func updateUIViewController(_: UIViewControllerType, context _: Context) {}
func makeCoordinator() -> Coordinator {
GifPickerView.Coordinator(parent: self)
}
class Coordinator: NSObject, GiphyDelegate {
var parent: GifPickerView
init(parent: GifPickerView) {
self.parent = parent
return controller
}
func didDismiss(controller _: GiphyViewController?) {
parent.onShouldDismissGifPicker()
func updateUIViewController(_: UIViewControllerType, context _: Context) {}
func makeCoordinator() -> Coordinator {
GifPickerView.Coordinator(parent: self)
}
func didSelectMedia(giphyViewController _: GiphyViewController, media: GPHMedia) {
let url = media.url(rendition: .fixedWidth, fileType: .gif)
parent.completion(url ?? "")
class Coordinator: NSObject, GiphyDelegate {
var parent: GifPickerView
init(parent: GifPickerView) {
self.parent = parent
}
func didDismiss(controller _: GiphyViewController?) {
parent.onShouldDismissGifPicker()
}
func didSelectMedia(giphyViewController _: GiphyViewController, media: GPHMedia) {
let url = media.url(rendition: .fixedWidth, fileType: .gif)
parent.completion(url ?? "")
}
}
}
}
#endif

View file

@ -1,10 +1,9 @@
import DesignSystem
import Env
import SwiftUI
import Models
import SwiftUI
extension StatusEditor {
@MainActor
struct LangButton: View {
@Environment(Theme.self) private var theme

View file

@ -13,5 +13,4 @@ extension StatusEditor {
let mediaAttachment: MediaAttachment?
let error: Error?
}
}

View file

@ -171,5 +171,4 @@ extension StatusEditor {
return translation?.content.asRawText
}
}
}

View file

@ -249,5 +249,4 @@ extension StatusEditor {
.cornerRadius(8)
}
}
}

View file

@ -121,5 +121,4 @@ extension StatusEditor {
return index == count - 1 && count < maxEntries
}
}
}

View file

@ -86,10 +86,10 @@ extension StatusEditor {
static var transferRepresentation: some TransferRepresentation {
FileRepresentation(importedContentType: .movie) { receivedTransferrable in
return MovieFileTranseferable(url: receivedTransferrable.localURL)
MovieFileTranseferable(url: receivedTransferrable.localURL)
}
FileRepresentation(importedContentType: .video) { receivedTransferrable in
return MovieFileTranseferable(url: receivedTransferrable.localURL)
MovieFileTranseferable(url: receivedTransferrable.localURL)
}
}
}
@ -112,7 +112,7 @@ extension StatusEditor {
static var transferRepresentation: some TransferRepresentation {
FileRepresentation(importedContentType: .gif) { receivedTransferrable in
return GifFileTranseferable(url: receivedTransferrable.localURL)
GifFileTranseferable(url: receivedTransferrable.localURL)
}
}
}
@ -133,7 +133,7 @@ public extension StatusEditor {
public static var transferRepresentation: some TransferRepresentation {
FileRepresentation(importedContentType: .image) { receivedTransferrable in
return ImageFileTranseferable(url: receivedTransferrable.localURL)
ImageFileTranseferable(url: receivedTransferrable.localURL)
}
}
}
@ -141,15 +141,15 @@ public extension StatusEditor {
public extension ReceivedTransferredFile {
var localURL: URL {
if self.isOriginalFile {
if isOriginalFile {
return file
}
let copy = URL.temporaryDirectory.appending(path: "\(UUID().uuidString).\(self.file.pathExtension)")
try? FileManager.default.copyItem(at: self.file, to: copy)
let copy = URL.temporaryDirectory.appending(path: "\(UUID().uuidString).\(file.pathExtension)")
try? FileManager.default.copyItem(at: file, to: copy)
return copy
}
}
public extension URL {
func mimeType() -> String {
if let mimeType = UTType(filenameExtension: pathExtension)?.preferredMIMEType {

View file

@ -49,5 +49,4 @@ extension StatusEditor {
}
}
}
}

Some files were not shown because too many files have changed in this diff Show more