From 1f858414d8590d179c75e6a1e3d8ba24af8b3f8c Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 14 Feb 2024 12:48:14 +0100 Subject: [PATCH] format . --- IceCubesApp/App/Main/AppView.swift | 86 ++++----- IceCubesApp/App/Main/IceCubesApp.swift | 4 +- IceCubesApp/App/Report/ReportView.swift | 52 +++--- IceCubesApp/App/SafariRouter.swift | 86 ++++----- IceCubesApp/App/SideBarView.swift | 6 +- IceCubesApp/App/Tabs/NavigationSheet.swift | 10 +- IceCubesApp/App/Tabs/NavigationTab.swift | 14 +- IceCubesApp/App/Tabs/NotificationTab.swift | 6 +- IceCubesApp/App/Tabs/Settings/AboutView.swift | 54 +++--- .../Tabs/Settings/AccountSettingView.swift | 4 +- .../App/Tabs/Settings/AddAccountsView.swift | 132 +++++++------- .../Tabs/Settings/ContentSettingsView.swift | 14 +- .../Tabs/Settings/DisplaySettingsView.swift | 64 +++---- .../Tabs/Settings/HapticSettingsView.swift | 4 +- .../App/Tabs/Settings/InstanceInfoView.swift | 4 +- .../Tabs/Settings/PushNotificationsView.swift | 10 +- .../Tabs/Settings/RecenTagsSettingView.swift | 22 +-- .../Settings/RemoteTimelinesSettingView.swift | 22 +-- .../App/Tabs/Settings/SettingsTab.swift | 34 ++-- .../Settings/SidebarEntriesSettingsView.swift | 8 +- .../App/Tabs/Settings/SupportAppView.swift | 34 ++-- .../Settings/SwipeActionsSettingsView.swift | 6 +- .../Settings/TabbarEntriesSettingsView.swift | 8 +- .../Tabs/Settings/TagsGroupSettingView.swift | 22 +-- .../Settings/TranslationSettingsView.swift | 14 +- IceCubesApp/App/Tabs/Tabs.swift | 35 ++-- .../App/Tabs/TagGroup/EditTagGroupView.swift | 24 +-- .../Tabs/Timeline/AddRemoteTimelineView.swift | 40 ++--- .../App/Tabs/Timeline/TimelineTab.swift | 22 +-- IceCubesApp/App/Tabs/ToolbarTab.swift | 13 +- .../NotificationServiceSupport.swift | 2 +- .../ShareViewController.swift | 2 +- Packages/Account/Package.swift | 2 +- .../Account/AccountDetailContextMenu.swift | 24 +-- .../Account/AccountDetailHeaderView.swift | 14 +- .../Sources/Account/AccountDetailView.swift | 32 ++-- .../Account/AccountDetailViewModel.swift | 27 +-- .../AccountsList/AccountsListView.swift | 22 +-- .../AccountsList/AccountsListViewModel.swift | 10 +- .../Account/Edit/EditAccountView.swift | 42 ++--- .../Account/Edit/EditAccountViewModel.swift | 41 ++--- .../Account/Filters/EditFilterView.swift | 22 +-- .../Account/Filters/FiltersListView.swift | 20 +-- .../Sources/Account/Follow/FollowButton.swift | 2 +- .../Sources/Account/Lists/ListsListView.swift | 5 +- .../AccountStatusesListView.swift | 50 +++--- .../AccountStatusesListViewModel.swift | 38 ++-- .../Account/Tags/FollowedTagsListView.swift | 9 +- .../Sources/AppAccount/AppAccountView.swift | 2 +- .../AppAccount/AppAccountsSelectorView.swift | 10 +- .../Detail/ConversationDetailView.swift | 50 +++--- .../Detail/ConversationMessageView.swift | 12 +- .../List/ConversationsListRow.swift | 2 +- .../Sources/DesignSystem/ColorSet.swift | 1 - .../DesignSystem/ConditionalModifier.swift | 12 +- .../Sources/DesignSystem/SceneDelegate.swift | 49 +++--- .../Sources/DesignSystem/Theme.swift | 12 +- .../Sources/DesignSystem/ThemeApplier.swift | 14 +- .../ToolbarItem/CancelToolbarItem.swift | 6 +- .../DesignSystem/Views/NextPageView.swift | 8 +- .../Env/Sources/Env/CustomEnvValues.swift | 4 +- Packages/Env/Sources/Env/HapticManager.swift | 66 +++---- Packages/Env/Sources/Env/Router.swift | 7 +- Packages/Env/Sources/Env/StreamWatcher.swift | 24 +-- .../Env/Sources/Env/UserPreferences.swift | 11 +- Packages/Env/Tests/RouterTests.swift | 12 +- .../Explore/Sources/Explore/ExploreView.swift | 122 ++++++------- .../Sources/Explore/TagsListView.swift | 4 +- .../Explore/TrendingLinksListView.swift | 6 +- .../MediaUI/Sources/MediaUI/DisplayType.swift | 2 +- .../MediaUI/MediaUIAttachmentVideoView.swift | 163 +++++++++--------- .../MediaUI/Sources/MediaUI/MediaUIView.swift | 2 +- .../MediaUI/QuickLookToolbarItem.swift | 4 +- Packages/Models/Sources/Models/Account.swift | 52 +++--- .../Sources/Models/Alias/ServerDate.swift | 6 +- .../Models/ConsolidatedNotification.swift | 4 +- Packages/Models/Sources/Models/List.swift | 2 +- Packages/Models/Sources/Models/Marker.swift | 2 +- .../Sources/Models/MediaAttachement.swift | 2 +- .../Models/Sources/Models/OauthToken.swift | 2 +- .../Models/Sources/Models/PostError.swift | 14 +- Packages/Models/Sources/Models/Status.swift | 4 +- .../Sources/Models/Stream/StreamEvent.swift | 2 +- Packages/Models/Sources/Models/Tag.swift | 2 +- Packages/Network/Sources/Network/Client.swift | 11 +- .../Sources/Network/Endpoint/Accounts.swift | 4 +- .../Network/InstanceSocialClient.swift | 4 +- .../Notifications/NotificationRowView.swift | 2 +- .../Notifications/NotificationsListView.swift | 92 +++++----- .../NotificationsViewModel.swift | 8 +- .../StatusKit/Detail/StatusDetailView.swift | 72 ++++---- .../Editor/Components/AIPrompt.swift | 1 - .../Editor/Components/AccessoryView.swift | 130 +++++++------- .../AutoComplete/AutoCompleteView.swift | 16 +- .../AutoComplete/ExpandedView.swift | 14 +- .../AutoComplete/MentionsView.swift | 9 +- .../AutoComplete/RecentTagsView.swift | 11 +- .../AutoComplete/RemoteTagsView.swift | 9 +- .../Editor/Components/CameraPickerView.swift | 3 +- .../CategorizedEmojiContainer.swift | 1 - .../Editor/Components/Compressor.swift | 9 +- .../Editor/Components/CustomEmojisView.swift | 7 +- .../Editor/Components/GIF/GIFPickerView.swift | 76 ++++---- .../Editor/Components/LangButton.swift | 13 +- .../Editor/Components/MediaContainer.swift | 1 - .../Editor/Components/MediaEditView.swift | 1 - .../Editor/Components/MediaView.swift | 1 - .../Editor/Components/PollView.swift | 1 - .../Editor/Components/UTTypeSupported.swift | 32 ++-- .../Editor/Drafts/DraftsListView.swift | 1 - .../StatusKit/Editor/EditorFocusState.swift | 1 - .../Sources/StatusKit/Editor/EditorView.swift | 20 +-- .../Sources/StatusKit/Editor/MainView.swift | 127 +++++++------- .../Sources/StatusKit/Editor/Namespace.swift | 2 +- .../StatusKit/Editor/PrivacyMenu.swift | 1 - .../StatusKit/Editor/ToolbarItems.swift | 10 +- .../Sources/StatusKit/Editor/ViewModel.swift | 55 +++--- .../StatusKit/Embed/StatusEmbeddedView.swift | 18 +- .../History/StatusEditHistoryView.swift | 2 +- .../LanguageDetection/LanguageDetection.swift | 2 +- .../StatusKit/List/StatusesListView.swift | 4 +- .../StatusKit/Poll/StatusPollView.swift | 15 +- .../StatusKit/Poll/StatusPollViewModel.swift | 2 +- .../Row/StatusRowAccessibilityLabel.swift | 2 +- .../Sources/StatusKit/Row/StatusRowView.swift | 39 +++-- .../Row/Subviews/StatusRowActionsView.swift | 62 +++---- .../Row/Subviews/StatusRowCardView.swift | 79 ++++----- .../Row/Subviews/StatusRowContextMenu.swift | 16 +- .../Row/Subviews/StatusRowHeaderView.swift | 35 ++-- .../Subviews/StatusRowMediaPreviewView.swift | 62 +++---- .../Row/Subviews/StatusRowTagView.swift | 11 +- .../Row/Subviews/StatusRowTextView.swift | 2 +- .../Timeline/TimelineContentFilter.swift | 12 +- .../Sources/Timeline/TimelineFilter.swift | 8 +- .../TimelineUnreadStatusesObserver.swift | 30 ++-- .../View/TimelineContentFilterView.swift | 8 +- .../Timeline/View/TimelineHeaderView.swift | 6 +- .../View/TimelineQuickAccessPills.swift | 41 ++--- .../View/TimelineTagGroupheaderView.swift | 8 +- .../Timeline/View/TimelineTagHeaderView.swift | 10 +- .../Sources/Timeline/View/TimelineView.swift | 29 ++-- .../Timeline/View/TimelineViewModel.swift | 36 ++-- .../Timeline/actors/TimelineCache.swift | 8 +- .../Timeline/actors/TimelineDatasource.swift | 15 +- .../TimelineTests/TimelineFilterTests.swift | 7 +- .../TimelineViewModelTests.swift | 18 +- 146 files changed, 1610 insertions(+), 1637 deletions(-) diff --git a/IceCubesApp/App/Main/AppView.swift b/IceCubesApp/App/Main/AppView.swift index c9cac65d..4b090762 100644 --- a/IceCubesApp/App/Main/AppView.swift +++ b/IceCubesApp/App/Main/AppView.swift @@ -17,29 +17,29 @@ struct AppView: View { @Environment(UserPreferences.self) private var userPreferences @Environment(Theme.self) private var theme @Environment(StreamWatcher.self) private var watcher - + @Environment(\.openWindow) var openWindow @Environment(\.horizontalSizeClass) private var horizontalSizeClass - + @Binding var selectedTab: Tab @Binding var appRouterPath: RouterPath - + @State var popToRootTab: Tab = .other @State var iosTabs = iOSTabs.shared @State var sidebarTabs = SidebarTabs.shared - + 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 } - + var availableTabs: [Tab] { guard appAccountsManager.currentClient.isAuth else { return Tab.loggedOutTab() @@ -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 } @@ -104,40 +104,40 @@ struct AppView: View { } return 0 } - + #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 { diff --git a/IceCubesApp/App/Main/IceCubesApp.swift b/IceCubesApp/App/Main/IceCubesApp.swift index 83f009ad..1d932d80 100644 --- a/IceCubesApp/App/Main/IceCubesApp.swift +++ b/IceCubesApp/App/Main/IceCubesApp.swift @@ -26,10 +26,10 @@ struct IceCubesApp: App { @State var watcher = StreamWatcher.shared @State var quickLook = QuickLook.shared @State var theme = Theme.shared - + @State var selectedTab: Tab = .timeline @State var appRouterPath = RouterPath() - + @State var isSupporter: Bool = false var body: some Scene { diff --git a/IceCubesApp/App/Report/ReportView.swift b/IceCubesApp/App/Report/ReportView.swift index af635a2d..dcb37d9a 100644 --- a/IceCubesApp/App/Report/ReportView.swift +++ b/IceCubesApp/App/Report/ReportView.swift @@ -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() + } } } } diff --git a/IceCubesApp/App/SafariRouter.swift b/IceCubesApp/App/SafariRouter.swift index 2882b676..9f07762a 100644 --- a/IceCubesApp/App/SafariRouter.swift +++ b/IceCubesApp/App/SafariRouter.swift @@ -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 { diff --git a/IceCubesApp/App/SideBarView.swift b/IceCubesApp/App/SideBarView.swift index 44574b73..1d1934d8 100644 --- a/IceCubesApp/App/SideBarView.swift +++ b/IceCubesApp/App/SideBarView.swift @@ -17,12 +17,12 @@ struct SideBarView: View { @Environment(StreamWatcher.self) private var watcher @Environment(UserPreferences.self) private var userPreferences @Environment(RouterPath.self) private var routerPath - + @Binding var selectedTab: Tab @Binding var popToRootTab: Tab var tabs: [Tab] @ViewBuilder var content: () -> Content - + @State private var sidebarTabs = SidebarTabs.shared private func badgeFor(tab: Tab) -> Int { @@ -166,7 +166,7 @@ struct SideBarView: View { .frame(width: .sidebarWidth) .background(.thinMaterial) }) - Divider().edgesIgnoringSafeArea(.all) + Divider().edgesIgnoringSafeArea(.all) } content() } diff --git a/IceCubesApp/App/Tabs/NavigationSheet.swift b/IceCubesApp/App/Tabs/NavigationSheet.swift index f581def3..a102145b 100644 --- a/IceCubesApp/App/Tabs/NavigationSheet.swift +++ b/IceCubesApp/App/Tabs/NavigationSheet.swift @@ -1,18 +1,18 @@ -import SwiftUI -import Env import AppAccount import DesignSystem +import Env +import SwiftUI @MainActor struct NavigationSheet: View { @Environment(\.dismiss) private var dismiss - + var content: () -> Content - + init(@ViewBuilder content: @escaping () -> Content) { self.content = content } - + var body: some View { NavigationStack { content() diff --git a/IceCubesApp/App/Tabs/NavigationTab.swift b/IceCubesApp/App/Tabs/NavigationTab.swift index 4e328afb..b3a19ccb 100644 --- a/IceCubesApp/App/Tabs/NavigationTab.swift +++ b/IceCubesApp/App/Tabs/NavigationTab.swift @@ -1,27 +1,27 @@ -import SwiftUI -import Env import AppAccount import DesignSystem +import Env import Network +import SwiftUI @MainActor struct NavigationTab: View { @Environment(\.isSecondaryColumn) private var isSecondaryColumn: Bool - + @Environment(AppAccountsManager.self) private var appAccount @Environment(CurrentAccount.self) private var currentAccount @Environment(UserPreferences.self) private var userPreferences @Environment(Theme.self) private var theme @Environment(Client.self) private var client - + var content: () -> Content - + @State private var routerPath = RouterPath() - + init(@ViewBuilder content: @escaping () -> Content) { self.content = content } - + var body: some View { NavigationStack(path: $routerPath.path) { content() diff --git a/IceCubesApp/App/Tabs/NotificationTab.swift b/IceCubesApp/App/Tabs/NotificationTab.swift index a2dc2c58..60713ee1 100644 --- a/IceCubesApp/App/Tabs/NotificationTab.swift +++ b/IceCubesApp/App/Tabs/NotificationTab.swift @@ -21,7 +21,7 @@ struct NotificationsTab: View { @Environment(PushNotificationsService.self) private var pushNotificationsService @State private var routerPath = RouterPath() @State private var scrollToTopSignal: Int = 0 - + @Binding var selectedTab: Tab @Binding var popToRootTab: Tab @@ -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) { diff --git a/IceCubesApp/App/Tabs/Settings/AboutView.swift b/IceCubesApp/App/Tabs/Settings/AboutView.swift index ed4d6304..a24d0198 100644 --- a/IceCubesApp/App/Tabs/Settings/AboutView.swift +++ b/IceCubesApp/App/Tabs/Settings/AboutView.swift @@ -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 diff --git a/IceCubesApp/App/Tabs/Settings/AccountSettingView.swift b/IceCubesApp/App/Tabs/Settings/AccountSettingView.swift index d8e3a5b5..9a9c5afc 100644 --- a/IceCubesApp/App/Tabs/Settings/AccountSettingView.swift +++ b/IceCubesApp/App/Tabs/Settings/AccountSettingView.swift @@ -116,8 +116,8 @@ struct AccountSettingsView: View { } .navigationTitle(account.safeDisplayName) #if !os(visionOS) - .scrollContentBackground(.hidden) - .background(theme.secondaryBackgroundColor) + .scrollContentBackground(.hidden) + .background(theme.secondaryBackgroundColor) #endif } } diff --git a/IceCubesApp/App/Tabs/Settings/AddAccountsView.swift b/IceCubesApp/App/Tabs/Settings/AddAccountsView.swift index cf28d3bd..c90cedeb 100644 --- a/IceCubesApp/App/Tabs/Settings/AddAccountsView.swift +++ b/IceCubesApp/App/Tabs/Settings/AddAccountsView.swift @@ -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 } diff --git a/IceCubesApp/App/Tabs/Settings/ContentSettingsView.swift b/IceCubesApp/App/Tabs/Settings/ContentSettingsView.swift index 8543fe43..ab561b5e 100644 --- a/IceCubesApp/App/Tabs/Settings/ContentSettingsView.swift +++ b/IceCubesApp/App/Tabs/Settings/ContentSettingsView.swift @@ -5,14 +5,14 @@ import Models import Network import NukeUI import SwiftUI -import UserNotifications import Timeline +import UserNotifications @MainActor struct ContentSettingsView: View { @Environment(UserPreferences.self) private var userPreferences @Environment(Theme.self) private var theme - + @State private var contentFilter = TimelineContentFilter.shared var body: some View { @@ -41,7 +41,7 @@ struct ContentSettingsView: View { } } } - #if !os(visionOS) + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) #endif @@ -113,7 +113,7 @@ struct ContentSettingsView: View { Text("settings.content.default-sensitive") } .disabled(userPreferences.useInstanceContentSettings) - + Toggle(isOn: $userPreferences.appRequireAltText) { Text("settings.content.require-alt-text") } @@ -121,7 +121,7 @@ struct ContentSettingsView: View { #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) #endif - + Section("timeline.content-filter.title") { Toggle(isOn: $contentFilter.showBoosts) { Label("timeline.filter.show-boosts", image: "Rocket") @@ -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 } } diff --git a/IceCubesApp/App/Tabs/Settings/DisplaySettingsView.swift b/IceCubesApp/App/Tabs/Settings/DisplaySettingsView.swift index c290fb39..64a59806 100644 --- a/IceCubesApp/App/Tabs/Settings/DisplaySettingsView.swift +++ b/IceCubesApp/App/Tabs/Settings/DisplaySettingsView.swift @@ -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 } } diff --git a/IceCubesApp/App/Tabs/Settings/HapticSettingsView.swift b/IceCubesApp/App/Tabs/Settings/HapticSettingsView.swift index 6c799c6a..e5e45e97 100644 --- a/IceCubesApp/App/Tabs/Settings/HapticSettingsView.swift +++ b/IceCubesApp/App/Tabs/Settings/HapticSettingsView.swift @@ -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 } } diff --git a/IceCubesApp/App/Tabs/Settings/InstanceInfoView.swift b/IceCubesApp/App/Tabs/Settings/InstanceInfoView.swift index 1118ae3f..987174cc 100644 --- a/IceCubesApp/App/Tabs/Settings/InstanceInfoView.swift +++ b/IceCubesApp/App/Tabs/Settings/InstanceInfoView.swift @@ -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 } } diff --git a/IceCubesApp/App/Tabs/Settings/PushNotificationsView.swift b/IceCubesApp/App/Tabs/Settings/PushNotificationsView.swift index 63b01aaa..57b5b65f 100644 --- a/IceCubesApp/App/Tabs/Settings/PushNotificationsView.swift +++ b/IceCubesApp/App/Tabs/Settings/PushNotificationsView.swift @@ -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() { diff --git a/IceCubesApp/App/Tabs/Settings/RecenTagsSettingView.swift b/IceCubesApp/App/Tabs/Settings/RecenTagsSettingView.swift index ec9aabdb..0f8ac598 100644 --- a/IceCubesApp/App/Tabs/Settings/RecenTagsSettingView.swift +++ b/IceCubesApp/App/Tabs/Settings/RecenTagsSettingView.swift @@ -1,17 +1,17 @@ -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 - + @Environment(RouterPath.self) private var routerPath @Environment(Theme.self) private var theme - + @Query(sort: \RecentTag.lastUse, order: .reverse) var tags: [RecentTag] - + var body: some View { Form { ForEach(tags) { tag in @@ -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() + } } } diff --git a/IceCubesApp/App/Tabs/Settings/RemoteTimelinesSettingView.swift b/IceCubesApp/App/Tabs/Settings/RemoteTimelinesSettingView.swift index 612e2285..94a35e49 100644 --- a/IceCubesApp/App/Tabs/Settings/RemoteTimelinesSettingView.swift +++ b/IceCubesApp/App/Tabs/Settings/RemoteTimelinesSettingView.swift @@ -1,17 +1,17 @@ -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 - + @Environment(RouterPath.self) private var routerPath @Environment(Theme.self) private var theme - + @Query(sort: \LocalTimeline.creationDate, order: .reverse) var localTimelines: [LocalTimeline] - + var body: some View { Form { ForEach(localTimelines) { timeline in @@ -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() + } } } diff --git a/IceCubesApp/App/Tabs/Settings/SettingsTab.swift b/IceCubesApp/App/Tabs/Settings/SettingsTab.swift index 9b4d9fb4..4724eb6b 100644 --- a/IceCubesApp/App/Tabs/Settings/SettingsTab.swift +++ b/IceCubesApp/App/Tabs/Settings/SettingsTab.swift @@ -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 diff --git a/IceCubesApp/App/Tabs/Settings/SidebarEntriesSettingsView.swift b/IceCubesApp/App/Tabs/Settings/SidebarEntriesSettingsView.swift index 790eeac8..15704630 100644 --- a/IceCubesApp/App/Tabs/Settings/SidebarEntriesSettingsView.swift +++ b/IceCubesApp/App/Tabs/Settings/SidebarEntriesSettingsView.swift @@ -5,7 +5,7 @@ import SwiftUI struct SidebarEntriesSettingsView: View { @Environment(Theme.self) private var theme @Environment(UserPreferences.self) private var userPreferences - + @State private var sidebarTabs = SidebarTabs.shared var body: some View { @@ -28,11 +28,11 @@ 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 } - + func move(from source: IndexSet, to destination: Int) { sidebarTabs.tabs.move(fromOffsets: source, toOffset: destination) } diff --git a/IceCubesApp/App/Tabs/Settings/SupportAppView.swift b/IceCubesApp/App/Tabs/Settings/SupportAppView.swift index 25d08b4a..bbe1b5f4 100644 --- a/IceCubesApp/App/Tabs/Settings/SupportAppView.swift +++ b/IceCubesApp/App/Tabs/Settings/SupportAppView.swift @@ -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 { diff --git a/IceCubesApp/App/Tabs/Settings/SwipeActionsSettingsView.swift b/IceCubesApp/App/Tabs/Settings/SwipeActionsSettingsView.swift index ef54893f..47443cc9 100644 --- a/IceCubesApp/App/Tabs/Settings/SwipeActionsSettingsView.swift +++ b/IceCubesApp/App/Tabs/Settings/SwipeActionsSettingsView.swift @@ -49,7 +49,7 @@ struct SwipeActionsSettingsView: View { #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) #endif - + Section { Picker(selection: $userPreferences.swipeActionsIconStyle, label: Text("settings.swipeactions.icon-style")) { ForEach(UserPreferences.SwipeActionsIconStyle.allCases, id: \.rawValue) { style in @@ -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 } diff --git a/IceCubesApp/App/Tabs/Settings/TabbarEntriesSettingsView.swift b/IceCubesApp/App/Tabs/Settings/TabbarEntriesSettingsView.swift index 0f26ffba..1b6c549d 100644 --- a/IceCubesApp/App/Tabs/Settings/TabbarEntriesSettingsView.swift +++ b/IceCubesApp/App/Tabs/Settings/TabbarEntriesSettingsView.swift @@ -6,7 +6,7 @@ import SwiftUI struct TabbarEntriesSettingsView: View { @Environment(Theme.self) private var theme @Environment(UserPreferences.self) private var userPreferences - + @State private var tabs = iOSTabs.shared var body: some View { @@ -42,7 +42,7 @@ struct TabbarEntriesSettingsView: View { #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) #endif - + Section { Toggle("settings.display.show-tab-label", isOn: $userPreferences.showiPhoneTabLabel) } @@ -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 } } diff --git a/IceCubesApp/App/Tabs/Settings/TagsGroupSettingView.swift b/IceCubesApp/App/Tabs/Settings/TagsGroupSettingView.swift index 812f8404..ba0b3995 100644 --- a/IceCubesApp/App/Tabs/Settings/TagsGroupSettingView.swift +++ b/IceCubesApp/App/Tabs/Settings/TagsGroupSettingView.swift @@ -1,17 +1,17 @@ -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 - + @Environment(RouterPath.self) private var routerPath @Environment(Theme.self) private var theme - + @Query(sort: \TagGroup.creationDate, order: .reverse) var tagGroups: [TagGroup] - + var body: some View { Form { ForEach(tagGroups) { group in @@ -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() + } } } diff --git a/IceCubesApp/App/Tabs/Settings/TranslationSettingsView.swift b/IceCubesApp/App/Tabs/Settings/TranslationSettingsView.swift index 838fa558..e844cc01 100644 --- a/IceCubesApp/App/Tabs/Settings/TranslationSettingsView.swift +++ b/IceCubesApp/App/Tabs/Settings/TranslationSettingsView.swift @@ -24,7 +24,7 @@ struct TranslationSettingsView: View { #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) #endif - + if apiKey.isEmpty { Section { Link(destination: URL(string: "https://www.deepl.com/pro-api")!) { @@ -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 diff --git a/IceCubesApp/App/Tabs/Tabs.swift b/IceCubesApp/App/Tabs/Tabs.swift index 0a401409..ee0c9491 100644 --- a/IceCubesApp/App/Tabs/Tabs.swift +++ b/IceCubesApp/App/Tabs/Tabs.swift @@ -24,7 +24,7 @@ enum Tab: Int, Identifiable, Hashable, CaseIterable, Codable { static func loggedOutTab() -> [Tab] { [.timeline, .settings] } - + static func visionOSTab() -> [Tab] { [.profile, .timeline, .notifications, .mentions, .explore, .post, .settings] } @@ -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() - } } @@ -164,7 +163,7 @@ class SidebarTabs { let tab: Tab var enabled: Bool } - + class Storage { @AppStorage("sidebar_tabs") var tabs: [SidedebarTab] = [ .init(tab: .timeline, enabled: true), @@ -179,25 +178,25 @@ class SidebarTabs { .init(tab: .favorites, enabled: true), .init(tab: .followedTags, enabled: true), .init(tab: .lists, enabled: true), - + .init(tab: .settings, enabled: true), .init(tab: .profile, enabled: true), ] } - + private let storage = Storage() public static let shared = SidebarTabs() - + var tabs: [SidedebarTab] { didSet { storage.tabs = tabs } } - + func isEnabled(_ tab: Tab) -> Bool { tabs.first(where: { $0.tab.id == tab.id })?.enabled == true } - + private init() { tabs = storage.tabs } @@ -208,7 +207,7 @@ class iOSTabs { enum TabEntries: String { case first, second, third, fourth, fifth } - + class Storage { @AppStorage(TabEntries.first.rawValue) var firstTab = Tab.timeline @AppStorage(TabEntries.second.rawValue) var secondTab = Tab.notifications @@ -216,44 +215,44 @@ class iOSTabs { @AppStorage(TabEntries.fourth.rawValue) var fourthTab = Tab.messages @AppStorage(TabEntries.fifth.rawValue) var fifthTab = Tab.profile } - + private let storage = Storage() public static let shared = iOSTabs() - + var tabs: [Tab] { [firstTab, secondTab, thirdTab, fourthTab, fifthTab] } - + var firstTab: Tab { didSet { storage.firstTab = firstTab } } - + var secondTab: Tab { didSet { storage.secondTab = secondTab } } - + var thirdTab: Tab { didSet { storage.thirdTab = thirdTab } } - + var fourthTab: Tab { didSet { storage.fourthTab = fourthTab } } - + var fifthTab: Tab { didSet { storage.fifthTab = fifthTab } } - + private init() { firstTab = storage.firstTab secondTab = storage.secondTab diff --git a/IceCubesApp/App/Tabs/TagGroup/EditTagGroupView.swift b/IceCubesApp/App/Tabs/TagGroup/EditTagGroupView.swift index 267a051c..5b8b7572 100644 --- a/IceCubesApp/App/Tabs/TagGroup/EditTagGroupView.swift +++ b/IceCubesApp/App/Tabs/TagGroup/EditTagGroupView.swift @@ -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 - } } } diff --git a/IceCubesApp/App/Tabs/Timeline/AddRemoteTimelineView.swift b/IceCubesApp/App/Tabs/Timeline/AddRemoteTimelineView.swift index 20094b44..3f6e7e38 100644 --- a/IceCubesApp/App/Tabs/Timeline/AddRemoteTimelineView.swift +++ b/IceCubesApp/App/Tabs/Timeline/AddRemoteTimelineView.swift @@ -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) + } } - } } } diff --git a/IceCubesApp/App/Tabs/Timeline/TimelineTab.swift b/IceCubesApp/App/Tabs/Timeline/TimelineTab.swift index f0b76c55..9e9fe8cc 100644 --- a/IceCubesApp/App/Tabs/Timeline/TimelineTab.swift +++ b/IceCubesApp/App/Tabs/Timeline/TimelineTab.swift @@ -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: @@ -185,7 +185,7 @@ struct TimelineTab: View { } } } - + @ViewBuilder private var headerGroup: some View { ControlGroup { @@ -209,10 +209,10 @@ struct TimelineTab: View { pinButton } } - + @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,14 +222,14 @@ struct TimelineTab: View { } } } label: { - if index != nil { + if index != nil { Label("status.action.unpin", systemImage: "pin.slash") } else { Label("status.action.pin", systemImage: "pin") } } } - + private var timelineFiltersButtons: some View { ForEach(TimelineFilter.availableTimeline(client: client), id: \.self) { timeline in Button { @@ -239,7 +239,7 @@ struct TimelineTab: View { } } } - + @ViewBuilder private var listsFiltersButons: some View { Menu("timeline.filter.lists") { @@ -257,7 +257,7 @@ struct TimelineTab: View { } } } - + @ViewBuilder private var tagsFiltersButtons: some View { if !currentAccount.tags.isEmpty { @@ -272,7 +272,7 @@ struct TimelineTab: View { } } } - + private var localTimelinesFiltersButtons: some View { Menu("timeline.filter.local") { ForEach(localTimelines) { remoteLocal in @@ -291,7 +291,7 @@ struct TimelineTab: View { } } } - + private var tagGroupsFiltersButtons: some View { Menu("timeline.filter.tag-groups") { ForEach(tagGroups) { group in @@ -312,7 +312,7 @@ struct TimelineTab: View { } } } - + private var contentFilterButton: some View { Button(action: { routerPath.presentedSheet = .timelineContentFilter diff --git a/IceCubesApp/App/Tabs/ToolbarTab.swift b/IceCubesApp/App/Tabs/ToolbarTab.swift index ff36b40d..5b084cbd 100644 --- a/IceCubesApp/App/Tabs/ToolbarTab.swift +++ b/IceCubesApp/App/Tabs/ToolbarTab.swift @@ -1,23 +1,24 @@ -import SwiftUI -import Env import AppAccount import DesignSystem +import Env +import SwiftUI @MainActor struct ToolbarTab: ToolbarContent { @Environment(\.isSecondaryColumn) private var isSecondaryColumn: Bool @Environment(\.horizontalSizeClass) private var horizontalSizeClass - + @Environment(UserPreferences.self) private var userPreferences - + @Binding var routerPath: RouterPath - + var body: some ToolbarContent { if !isSecondaryColumn { 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) } diff --git a/IceCubesNotifications/NotificationServiceSupport.swift b/IceCubesNotifications/NotificationServiceSupport.swift index 6b5dde4f..889af66f 100644 --- a/IceCubesNotifications/NotificationServiceSupport.swift +++ b/IceCubesNotifications/NotificationServiceSupport.swift @@ -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 } diff --git a/IceCubesShareExtension/ShareViewController.swift b/IceCubesShareExtension/ShareViewController.swift index dd0c316f..7e66786b 100644 --- a/IceCubesShareExtension/ShareViewController.swift +++ b/IceCubesShareExtension/ShareViewController.swift @@ -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() { diff --git a/Packages/Account/Package.swift b/Packages/Account/Package.swift index 653cb030..05638ddd 100644 --- a/Packages/Account/Package.swift +++ b/Packages/Account/Package.swift @@ -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"), diff --git a/Packages/Account/Sources/Account/AccountDetailContextMenu.swift b/Packages/Account/Sources/Account/AccountDetailContextMenu.swift index 3c592abe..cba82d4f 100644 --- a/Packages/Account/Sources/Account/AccountDetailContextMenu.swift +++ b/Packages/Account/Sources/Account/AccountDetailContextMenu.swift @@ -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 } } diff --git a/Packages/Account/Sources/Account/AccountDetailHeaderView.swift b/Packages/Account/Sources/Account/AccountDetailHeaderView.swift index 1cc7be8a..1c636e51 100644 --- a/Packages/Account/Sources/Account/AccountDetailHeaderView.swift +++ b/Packages/Account/Sources/Account/AccountDetailHeaderView.swift @@ -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) + ) } } } diff --git a/Packages/Account/Sources/Account/AccountDetailView.swift b/Packages/Account/Sources/Account/AccountDetailView.swift index 725a33b9..fc89cef4 100644 --- a/Packages/Account/Sources/Account/AccountDetailView.swift +++ b/Packages/Account/Sources/Account/AccountDetailView.swift @@ -27,7 +27,7 @@ public struct AccountDetailView: View { @State private var isEditingAccount: Bool = false @State private var isEditingFilters: Bool = false @State private var isEditingRelationshipNote: Bool = false - + @State private var displayTitle: Bool = false @Binding var scrollToTopSignal: Int @@ -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 } } diff --git a/Packages/Account/Sources/Account/AccountDetailViewModel.swift b/Packages/Account/Sources/Account/AccountDetailViewModel.swift index 7dd2c495..81c855a4 100644 --- a/Packages/Account/Sources/Account/AccountDetailViewModel.swift +++ b/Packages/Account/Sources/Account/AccountDetailViewModel.swift @@ -84,7 +84,7 @@ import SwiftUI private var tabTask: Task? private(set) var statuses: [Status] = [] - + var boosts: [Status] = [] /// When coming from a URL like a mention tap in a status. @@ -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) } diff --git a/Packages/Account/Sources/Account/AccountsList/AccountsListView.swift b/Packages/Account/Sources/Account/AccountsList/AccountsListView.swift index 04296b66..98aa6ced 100644 --- a/Packages/Account/Sources/Account/AccountsList/AccountsListView.swift +++ b/Packages/Account/Sources/Account/AccountsList/AccountsListView.swift @@ -45,7 +45,7 @@ public struct AccountsListView: View { await viewModel.fetch() } } - + @ViewBuilder private var listView: some View { if currentAccount.account?.id == viewModel.accountId { @@ -54,7 +54,7 @@ public struct AccountsListView: View { standardList } } - + private var searchableList: some View { List { listContent @@ -74,13 +74,13 @@ public struct AccountsListView: View { } } } - + private var standardList: some View { List { listContent } } - + @ViewBuilder private var listContent: some View { switch viewModel.state { @@ -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 } } } @@ -140,16 +140,16 @@ public struct AccountsListView: View { #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) #endif - + case .none: EmptyView() } case let .error(error): Text(error.localizedDescription) - #if !os(visionOS) + #if !os(visionOS) .listRowBackground(theme.primaryBackgroundColor) - #endif + #endif } } } diff --git a/Packages/Account/Sources/Account/AccountsList/AccountsListViewModel.swift b/Packages/Account/Sources/Account/AccountsList/AccountsListViewModel.swift index 1cc51ae5..b6db3470 100644 --- a/Packages/Account/Sources/Account/AccountsList/AccountsListViewModel.swift +++ b/Packages/Account/Sources/Account/AccountsList/AccountsListViewModel.swift @@ -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) @@ -49,7 +49,7 @@ public enum AccountsListMode { var state = State.loading var totalCount: Int? var accountId: String? - + var searchQuery: String = "" private var nextPageId: String? @@ -125,7 +125,7 @@ public enum AccountsListMode { relationships: relationships, nextPageState: link?.maxId != nil ? .hasNextPage : .none) } - + func search() async { guard let client, !searchQuery.isEmpty else { return } do { @@ -144,8 +144,6 @@ public enum AccountsListMode { relationships: relationships, nextPageState: .none) } - } catch { - - } + } catch {} } } diff --git a/Packages/Account/Sources/Account/Edit/EditAccountView.swift b/Packages/Account/Sources/Account/Edit/EditAccountView.swift index 6e9a3675..69aa5d06 100644 --- a/Packages/Account/Sources/Account/Edit/EditAccountView.swift +++ b/Packages/Account/Sources/Account/Edit/EditAccountView.swift @@ -2,8 +2,8 @@ import DesignSystem import Env import Models import Network -import SwiftUI import NukeUI +import SwiftUI @MainActor public struct EditAccountView: View { @@ -13,8 +13,8 @@ public struct EditAccountView: View { @Environment(UserPreferences.self) private var userPrefs @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() + } } } @@ -64,7 +64,7 @@ public struct EditAccountView: View { .listRowBackground(theme.primaryBackgroundColor) #endif } - + private var imagesSection: some View { Section { ZStack(alignment: .center) { diff --git a/Packages/Account/Sources/Account/Edit/EditAccountViewModel.swift b/Packages/Account/Sources/Account/Edit/EditAccountViewModel.swift index 25323a56..221b0b31 100644 --- a/Packages/Account/Sources/Account/Edit/EditAccountViewModel.swift +++ b/Packages/Account/Sources/Account/Edit/EditAccountViewModel.swift @@ -1,9 +1,9 @@ import Models import Network import Observation -import SwiftUI import PhotosUI import StatusKit +import SwiftUI @MainActor @Observable class EditAccountViewModel { @@ -30,22 +30,23 @@ import StatusKit var fields: [FieldEditViewModel] = [] var avatar: URL? var header: URL? - + 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 var isLoading: Bool = true var isSaving: Bool = false var saveError: Bool = false - + var mediaPickers: [PhotosPickerItem] = [] { didSet { if let item = mediaPickers.first { @@ -108,47 +109,47 @@ import StatusKit saveError = true } } - + private func uploadHeader(data: Data) async -> Bool { 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 } } - + private func uploadAvatar(data: Data) async -> Bool { 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 } } - + private func getItemImageData(item: PhotosPickerItem) async -> Data? { guard let imageFile = try? await item.loadTransferable(type: StatusEditor.ImageFileTranseferable.self) else { return nil } 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 } } diff --git a/Packages/Account/Sources/Account/Filters/EditFilterView.swift b/Packages/Account/Sources/Account/Filters/EditFilterView.swift index e30bff23..2539ff8c 100644 --- a/Packages/Account/Sources/Account/Filters/EditFilterView.swift +++ b/Packages/Account/Sources/Account/Filters/EditFilterView.swift @@ -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 { diff --git a/Packages/Account/Sources/Account/Filters/FiltersListView.swift b/Packages/Account/Sources/Account/Filters/FiltersListView.swift index 6f10bb03..75628d38 100644 --- a/Packages/Account/Sources/Account/Filters/FiltersListView.swift +++ b/Packages/Account/Sources/Account/Filters/FiltersListView.swift @@ -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 + } } - } } } diff --git a/Packages/Account/Sources/Account/Follow/FollowButton.swift b/Packages/Account/Sources/Account/Follow/FollowButton.swift index 328df665..841c466f 100644 --- a/Packages/Account/Sources/Account/Follow/FollowButton.swift +++ b/Packages/Account/Sources/Account/Follow/FollowButton.swift @@ -4,8 +4,8 @@ import Foundation import Models import Network import Observation -import SwiftUI import OSLog +import SwiftUI @MainActor @Observable public class FollowButtonViewModel { diff --git a/Packages/Account/Sources/Account/Lists/ListsListView.swift b/Packages/Account/Sources/Account/Lists/ListsListView.swift index b9fa1995..766081e6 100644 --- a/Packages/Account/Sources/Account/Lists/ListsListView.swift +++ b/Packages/Account/Sources/Account/Lists/ListsListView.swift @@ -1,14 +1,14 @@ import DesignSystem +import Env import Models import SwiftUI -import Env public struct ListsListView: View { @Environment(CurrentAccount.self) private var currentAccount @Environment(Theme.self) private var theme public init() {} - + public var body: some View { List { ForEach(currentAccount.lists) { list in @@ -43,4 +43,3 @@ public struct ListsListView: View { .navigationBarTitleDisplayMode(.inline) } } - diff --git a/Packages/Account/Sources/Account/StatusesLists/AccountStatusesListView.swift b/Packages/Account/Sources/Account/StatusesLists/AccountStatusesListView.swift index cdd6b93d..cf89590a 100644 --- a/Packages/Account/Sources/Account/StatusesLists/AccountStatusesListView.swift +++ b/Packages/Account/Sources/Account/StatusesLists/AccountStatusesListView.swift @@ -1,50 +1,50 @@ -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 { @Environment(Theme.self) private var theme @Environment(Client.self) private var client @Environment(RouterPath.self) private var routerPath - + @State private var viewModel: AccountStatusesListViewModel @State private var isLoaded = false - + public init(mode: AccountStatusesListViewModel.Mode) { _viewModel = .init(initialValue: .init(mode: mode)) } - + public var body: some View { List { StatusesListView(fetcher: viewModel, client: client, routerPath: routerPath) } .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 + } + } } } diff --git a/Packages/Account/Sources/Account/StatusesLists/AccountStatusesListViewModel.swift b/Packages/Account/Sources/Account/StatusesLists/AccountStatusesListViewModel.swift index cf56fdfd..c617efd5 100644 --- a/Packages/Account/Sources/Account/StatusesLists/AccountStatusesListViewModel.swift +++ b/Packages/Account/Sources/Account/StatusesLists/AccountStatusesListViewModel.swift @@ -1,15 +1,15 @@ -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 { switch self { case .bookmarks: @@ -18,7 +18,7 @@ public class AccountStatusesListViewModel: StatusesFetcher { "accessibility.tabs.profile.picker.favorites" } } - + func endpoint(sinceId: String?) -> Endpoint { switch self { case .bookmarks: @@ -28,19 +28,19 @@ public class AccountStatusesListViewModel: StatusesFetcher { } } } - + let mode: Mode public var statusesState: StatusesState = .loading var statuses: [Status] = [] var nextPage: LinkHandler? - + var client: Client? - + init(mode: Mode) { self.mode = mode } - - public func fetchNewestStatuses(pullToRefresh: Bool) async { + + public func fetchNewestStatuses(pullToRefresh _: Bool) async { guard let client else { return } statusesState = .loading do { @@ -52,7 +52,7 @@ public class AccountStatusesListViewModel: StatusesFetcher { statusesState = .error(error: error) } } - + public func fetchNextPage() async throws { guard let client, let nextId = nextPage?.maxId else { return } var newStatuses: [Status] = [] @@ -62,12 +62,8 @@ public class AccountStatusesListViewModel: StatusesFetcher { statusesState = .display(statuses: statuses, nextPageState: nextPage?.maxId != nil ? .hasNextPage : .none) } - - public func statusDidAppear(status: Status) { - - } - - public func statusDidDisappear(status: Status) { - - } + + public func statusDidAppear(status _: Status) {} + + public func statusDidDisappear(status _: Status) {} } diff --git a/Packages/Account/Sources/Account/Tags/FollowedTagsListView.swift b/Packages/Account/Sources/Account/Tags/FollowedTagsListView.swift index 7cbb8ed4..ca3a9180 100644 --- a/Packages/Account/Sources/Account/Tags/FollowedTagsListView.swift +++ b/Packages/Account/Sources/Account/Tags/FollowedTagsListView.swift @@ -1,20 +1,20 @@ import DesignSystem +import Env import Models import SwiftUI -import Env public struct FollowedTagsListView: View { @Environment(CurrentAccount.self) private var currentAccount @Environment(Theme.self) private var theme public init() {} - + 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) } } - diff --git a/Packages/AppAccount/Sources/AppAccount/AppAccountView.swift b/Packages/AppAccount/Sources/AppAccount/AppAccountView.swift index f39c63ce..c2639727 100644 --- a/Packages/AppAccount/Sources/AppAccount/AppAccountView.swift +++ b/Packages/AppAccount/Sources/AppAccount/AppAccountView.swift @@ -13,7 +13,7 @@ public struct AppAccountView: View { @State var viewModel: AppAccountViewModel @Binding var isParentPresented: Bool - + public init(viewModel: AppAccountViewModel, isParentPresented: Binding) { self.viewModel = viewModel _isParentPresented = isParentPresented diff --git a/Packages/AppAccount/Sources/AppAccount/AppAccountsSelectorView.swift b/Packages/AppAccount/Sources/AppAccount/AppAccountsSelectorView.swift index f0d2d051..9da47fb4 100644 --- a/Packages/AppAccount/Sources/AppAccount/AppAccountsSelectorView.swift +++ b/Packages/AppAccount/Sources/AppAccount/AppAccountsSelectorView.swift @@ -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 { @@ -134,7 +134,7 @@ public struct AppAccountsSelectorView: View { .environment(routerPath) } } - + private var addAccountButton: some View { Button { isPresented = false @@ -158,7 +158,7 @@ public struct AppAccountsSelectorView: View { Label("tab.settings", systemImage: "gear") } } - + private var supportButton: some View { Button { isPresented = false @@ -170,7 +170,7 @@ public struct AppAccountsSelectorView: View { Label("settings.app.support", systemImage: "wand.and.stars") } } - + private var aboutButton: some View { Button { isPresented = false diff --git a/Packages/Conversations/Sources/Conversations/Detail/ConversationDetailView.swift b/Packages/Conversations/Sources/Conversations/Detail/ConversationDetailView.swift index 999b861b..cf345a72 100644 --- a/Packages/Conversations/Sources/Conversations/Detail/ConversationDetailView.swift +++ b/Packages/Conversations/Sources/Conversations/Detail/ConversationDetailView.swift @@ -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 { diff --git a/Packages/Conversations/Sources/Conversations/Detail/ConversationMessageView.swift b/Packages/Conversations/Sources/Conversations/Detail/ConversationMessageView.swift index f53dd1ad..2621c984 100644 --- a/Packages/Conversations/Sources/Conversations/Detail/ConversationMessageView.swift +++ b/Packages/Conversations/Sources/Conversations/Detail/ConversationMessageView.swift @@ -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 } } diff --git a/Packages/Conversations/Sources/Conversations/List/ConversationsListRow.swift b/Packages/Conversations/Sources/Conversations/List/ConversationsListRow.swift index c212f27b..f546a1b4 100644 --- a/Packages/Conversations/Sources/Conversations/List/ConversationsListRow.swift +++ b/Packages/Conversations/Sources/Conversations/List/ConversationsListRow.swift @@ -7,7 +7,7 @@ import SwiftUI @MainActor struct ConversationsListRow: View { @Environment(\.openWindow) private var openWindow - + @Environment(Client.self) private var client @Environment(RouterPath.self) private var routerPath @Environment(Theme.self) private var theme diff --git a/Packages/DesignSystem/Sources/DesignSystem/ColorSet.swift b/Packages/DesignSystem/Sources/DesignSystem/ColorSet.swift index 60dd6042..1b6d8efa 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/ColorSet.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/ColorSet.swift @@ -201,4 +201,3 @@ public struct ThreadsLight: ColorSet { public init() {} } - diff --git a/Packages/DesignSystem/Sources/DesignSystem/ConditionalModifier.swift b/Packages/DesignSystem/Sources/DesignSystem/ConditionalModifier.swift index be1f25a1..98d56213 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/ConditionalModifier.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/ConditionalModifier.swift @@ -1,11 +1,11 @@ import SwiftUI public extension View { - @ViewBuilder func `if`(_ condition: Bool, transform: (Self) -> Content) -> some View { - if condition { - transform(self) - } else { - self - } + @ViewBuilder func `if`(_ condition: Bool, transform: (Self) -> Content) -> some View { + if condition { + transform(self) + } else { + self } + } } diff --git a/Packages/DesignSystem/Sources/DesignSystem/SceneDelegate.swift b/Packages/DesignSystem/Sources/DesignSystem/SceneDelegate.swift index 67edbd55..45858bb6 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/SceneDelegate.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/SceneDelegate.swift @@ -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 - } } } diff --git a/Packages/DesignSystem/Sources/DesignSystem/Theme.swift b/Packages/DesignSystem/Sources/DesignSystem/Theme.swift index 952e776d..a3df5acf 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/Theme.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/Theme.swift @@ -69,10 +69,10 @@ import SwiftUI } } } - + public enum StatusActionSecondary: String, CaseIterable { case share, bookmark - + public var description: LocalizedStringKey { switch self { case .share: @@ -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()) @@ -244,7 +244,7 @@ import SwiftUI themeStorage.statusDisplayStyle = statusDisplayStyle } } - + public var statusActionSecondary: StatusActionSecondary { didSet { themeStorage.statusActionSecondary = statusActionSecondary @@ -300,7 +300,7 @@ import SwiftUI chosenFontData = nil statusActionSecondary = .share } - + private init() { isThemePreviouslySet = themeStorage.isThemePreviouslySet selectedScheme = themeStorage.selectedScheme @@ -340,7 +340,7 @@ import SwiftUI ConstellationLight(), ConstellationDark(), ThreadsLight(), - ThreadsDark() + ThreadsDark(), ] } diff --git a/Packages/DesignSystem/Sources/DesignSystem/ThemeApplier.swift b/Packages/DesignSystem/Sources/DesignSystem/ThemeApplier.swift index cf47fab9..6f0da968 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/ThemeApplier.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/ThemeApplier.swift @@ -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) { diff --git a/Packages/DesignSystem/Sources/DesignSystem/ToolbarItem/CancelToolbarItem.swift b/Packages/DesignSystem/Sources/DesignSystem/ToolbarItem/CancelToolbarItem.swift index d4880a01..6acf1b7e 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/ToolbarItem/CancelToolbarItem.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/ToolbarItem/CancelToolbarItem.swift @@ -2,9 +2,9 @@ import SwiftUI public struct CancelToolbarItem: ToolbarContent { @Environment(\.dismiss) private var dismiss - - public init() { } - + + public init() {} + public var body: some ToolbarContent { ToolbarItem(placement: .navigationBarLeading) { Button("action.cancel", role: .cancel, action: { dismiss() }) diff --git a/Packages/DesignSystem/Sources/DesignSystem/Views/NextPageView.swift b/Packages/DesignSystem/Sources/DesignSystem/Views/NextPageView.swift index 80c7c561..04bb593f 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/Views/NextPageView.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/Views/NextPageView.swift @@ -4,12 +4,12 @@ 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 } - + public var body: some View { HStack { if showRetry { @@ -35,7 +35,7 @@ public struct NextPageView: View { } .listRowSeparator(.hidden, edges: .all) } - + private func executeTask() async { showRetry = false defer { diff --git a/Packages/Env/Sources/Env/CustomEnvValues.swift b/Packages/Env/Sources/Env/CustomEnvValues.swift index 21db4121..2911b5c8 100644 --- a/Packages/Env/Sources/Env/CustomEnvValues.swift +++ b/Packages/Env/Sources/Env/CustomEnvValues.swift @@ -56,12 +56,12 @@ public extension EnvironmentValues { get { self[IsCompact.self] } set { self[IsCompact.self] = newValue } } - + var isMediaCompact: Bool { get { self[IsMediaCompact.self] } set { self[IsMediaCompact.self] = newValue } } - + var isModal: Bool { get { self[IsModal.self] } set { self[IsModal.self] = newValue } diff --git a/Packages/Env/Sources/Env/HapticManager.swift b/Packages/Env/Sources/Env/HapticManager.swift index bf98c32a..d32a9b80 100644 --- a/Packages/Env/Sources/Env/HapticManager.swift +++ b/Packages/Env/Sources/Env/HapticManager.swift @@ -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 } diff --git a/Packages/Env/Sources/Env/Router.swift b/Packages/Env/Sources/Env/Router.swift index 4315ddeb..42b8c640 100644 --- a/Packages/Env/Sources/Env/Router.swift +++ b/Packages/Env/Sources/Env/Router.swift @@ -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) diff --git a/Packages/Env/Sources/Env/StreamWatcher.swift b/Packages/Env/Sources/Env/StreamWatcher.swift index e844b356..f8690bab 100644 --- a/Packages/Env/Sources/Env/StreamWatcher.swift +++ b/Packages/Env/Sources/Env/StreamWatcher.swift @@ -27,11 +27,11 @@ import OSLog public var events: [any StreamEvent] = [] public var unreadNotificationsCount: Int = 0 public var latestEvent: (any StreamEvent)? - + private let logger = Logger(subsystem: "com.icecubesapp", category: "stream") public static let shared = StreamWatcher() - + private init() { decoder.keyDecodingStrategy = .convertFromSnakeCase } @@ -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)) } } @@ -156,22 +156,22 @@ import OSLog return nil } } - + 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 } } diff --git a/Packages/Env/Sources/Env/UserPreferences.swift b/Packages/Env/Sources/Env/UserPreferences.swift index 427378e2..3f3a83ed 100644 --- a/Packages/Env/Sources/Env/UserPreferences.swift +++ b/Packages/Env/Sources/Env/UserPreferences.swift @@ -53,7 +53,7 @@ import SwiftUI @AppStorage("collapse-long-posts") public var collapseLongPosts = true @AppStorage("share-button-behavior") public var shareButtonBehavior: PreferredShareButtonBehavior = .linkOnly - + @AppStorage("fast_refresh") public var fastRefreshEnabled: Bool = false @AppStorage("max_reply_indentation") public var maxReplyIndentation: UInt = 7 @@ -164,7 +164,7 @@ import SwiftUI storage.appDefaultPostsSensitive = appDefaultPostsSensitive } } - + public var appRequireAltText: Bool { didSet { storage.appRequireAltText = appRequireAltText @@ -183,7 +183,6 @@ import SwiftUI } } - public var alwaysUseDeepl: Bool { didSet { storage.alwaysUseDeepl = alwaysUseDeepl @@ -303,7 +302,7 @@ import SwiftUI storage.shareButtonBehavior = shareButtonBehavior } } - + public var fastRefreshEnabled: Bool { didSet { storage.fastRefreshEnabled = fastRefreshEnabled @@ -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]) { @@ -509,7 +508,7 @@ extension UInt: RawRepresentable { public var rawValue: Int { Int(self) } - + public init?(rawValue: Int) { if rawValue >= 0 { self.init(rawValue) diff --git a/Packages/Env/Tests/RouterTests.swift b/Packages/Env/Tests/RouterTests.swift index 04b60ef9..d14fd627 100644 --- a/Packages/Env/Tests/RouterTests.swift +++ b/Packages/Env/Tests/RouterTests.swift @@ -1,7 +1,7 @@ @testable import Env -import XCTest -import SwiftUI import Network +import SwiftUI +import XCTest @MainActor final class RouterTests: XCTestCase { @@ -11,14 +11,14 @@ final class RouterTests: XCTestCase { _ = router.handle(url: url) XCTAssertTrue(router.path.isEmpty) } - + func testRouterTagsURL() { let router = RouterPath() let url = URL(string: "https://mastodon.social/tags/test")! _ = router.handle(url: url) XCTAssertTrue(router.path.first == .hashTag(tag: "test", account: nil)) } - + func testRouterLocalStatusURL() { let router = RouterPath() let client = Client(server: "mastodon.social", @@ -29,7 +29,7 @@ final class RouterTests: XCTestCase { _ = router.handle(url: url) XCTAssertTrue(router.path.first == .statusDetail(id: "1010384")) } - + func testRouterRemoteStatusURL() { let router = RouterPath() let client = Client(server: "mastodon.social", @@ -40,7 +40,7 @@ final class RouterTests: XCTestCase { _ = router.handle(url: url) XCTAssertTrue(router.path.first == .remoteStatusDetail(url: url)) } - + func testRouteRandomURL() { let router = RouterPath() let url = URL(string: "https://theweb.com/test/test/one")! diff --git a/Packages/Explore/Sources/Explore/ExploreView.swift b/Packages/Explore/Sources/Explore/ExploreView.swift index 3d551385..0fa237c3 100644 --- a/Packages/Explore/Sources/Explore/ExploreView.swift +++ b/Packages/Explore/Sources/Explore/ExploreView.swift @@ -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 } diff --git a/Packages/Explore/Sources/Explore/TagsListView.swift b/Packages/Explore/Sources/Explore/TagsListView.swift index de49a4ec..7722b345 100644 --- a/Packages/Explore/Sources/Explore/TagsListView.swift +++ b/Packages/Explore/Sources/Explore/TagsListView.swift @@ -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) } } diff --git a/Packages/Explore/Sources/Explore/TrendingLinksListView.swift b/Packages/Explore/Sources/Explore/TrendingLinksListView.swift index b3f80fb9..79018d4a 100644 --- a/Packages/Explore/Sources/Explore/TrendingLinksListView.swift +++ b/Packages/Explore/Sources/Explore/TrendingLinksListView.swift @@ -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 { diff --git a/Packages/MediaUI/Sources/MediaUI/DisplayType.swift b/Packages/MediaUI/Sources/MediaUI/DisplayType.swift index 99a143ec..371e56a5 100644 --- a/Packages/MediaUI/Sources/MediaUI/DisplayType.swift +++ b/Packages/MediaUI/Sources/MediaUI/DisplayType.swift @@ -1,5 +1,5 @@ -import SwiftUI import Models +import SwiftUI enum DisplayType { case image diff --git a/Packages/MediaUI/Sources/MediaUI/MediaUIAttachmentVideoView.swift b/Packages/MediaUI/Sources/MediaUI/MediaUIAttachmentVideoView.swift index 6746be22..b23a11b9 100644 --- a/Packages/MediaUI/Sources/MediaUI/MediaUIAttachmentVideoView.swift +++ b/Packages/MediaUI/Sources/MediaUI/MediaUIAttachmentVideoView.swift @@ -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 { @@ -41,7 +41,7 @@ import Models } } } - + func mute(_ mute: Bool) { player?.isMuted = mute } @@ -50,7 +50,7 @@ import Models isPlaying = false player?.pause() } - + func stop() { isPlaying = false player?.pause() @@ -62,15 +62,15 @@ import Models player?.seek(to: CMTime.zero) player?.play() } - + func resume() { isPlaying = true player?.play() } - + 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, + if !preferences.autoPlayVideo, + !viewModel.forceAutoPlay, !isFullScreen, - !viewModel.isPlaying, - !isCompact { + !viewModel.isPlaying, + !isCompact + { Button(action: { viewModel.play() }, label: { diff --git a/Packages/MediaUI/Sources/MediaUI/MediaUIView.swift b/Packages/MediaUI/Sources/MediaUI/MediaUIView.swift index 64c416c7..8bdc8cbf 100644 --- a/Packages/MediaUI/Sources/MediaUI/MediaUIView.swift +++ b/Packages/MediaUI/Sources/MediaUI/MediaUIView.swift @@ -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] diff --git a/Packages/MediaUI/Sources/MediaUI/QuickLookToolbarItem.swift b/Packages/MediaUI/Sources/MediaUI/QuickLookToolbarItem.swift index 3cf4fbe3..d34b95f4 100644 --- a/Packages/MediaUI/Sources/MediaUI/QuickLookToolbarItem.swift +++ b/Packages/MediaUI/Sources/MediaUI/QuickLookToolbarItem.swift @@ -1,6 +1,6 @@ -import SwiftUI -import NukeUI import Nuke +import NukeUI +import SwiftUI struct QuickLookToolbarItem: ToolbarContent, @unchecked Sendable { let itemUrl: URL diff --git a/Packages/Models/Sources/Models/Account.swift b/Packages/Models/Sources/Models/Account.swift index 3aa29952..b222f79a 100644 --- a/Packages/Models/Sources/Models/Account.swift +++ b/Packages/Models/Sources/Models/Account.swift @@ -90,14 +90,14 @@ public final class Account: Codable, Identifiable, Hashable, Sendable, Equatable self.source = source self.bot = bot 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)") } } - + public enum CodingKeys: CodingKey { case id case username @@ -119,32 +119,32 @@ public final class Account: Codable, Identifiable, Hashable, Sendable, Equatable case bot case discoverable } - + 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)") } } diff --git a/Packages/Models/Sources/Models/Alias/ServerDate.swift b/Packages/Models/Sources/Models/Alias/ServerDate.swift index 986e19d4..9e1aa4b1 100644 --- a/Packages/Models/Sources/Models/Alias/ServerDate.swift +++ b/Packages/Models/Sources/Models/Alias/ServerDate.swift @@ -15,14 +15,14 @@ public struct ServerDate: Codable, Hashable, Equatable, Sendable { relativeTo: Date()) } else { return Duration.seconds(-date.timeIntervalSinceNow).formatted(.units(width: .narrow, - maximumUnitCount: 1)) + maximumUnitCount: 1)) } } public var shortDateFormatted: String { DateFormatterCache.shared.createdAtShortDateFormatted.string(from: asDate) } - + private static let calendar = Calendar(identifier: .gregorian) public init() { @@ -41,7 +41,7 @@ public struct ServerDate: Codable, Hashable, Equatable, Sendable { let container = try decoder.container(keyedBy: CodingKeys.self) asDate = try container.decode(Date.self, forKey: .asDate) } - + let aDay: TimeInterval = 60 * 60 * 24 isOlderThanADay = Date().timeIntervalSince(asDate) >= aDay } diff --git a/Packages/Models/Sources/Models/ConsolidatedNotification.swift b/Packages/Models/Sources/Models/ConsolidatedNotification.swift index 50d88f98..6c8c9677 100644 --- a/Packages/Models/Sources/Models/ConsolidatedNotification.swift +++ b/Packages/Models/Sources/Models/ConsolidatedNotification.swift @@ -38,8 +38,8 @@ public struct ConsolidatedNotification: Identifiable { } public static func placeholders() -> [ConsolidatedNotification] { - [.placeholder(), .placeholder(), .placeholder(), - .placeholder(), .placeholder(), .placeholder(), + [.placeholder(), .placeholder(), .placeholder(), + .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder()] } diff --git a/Packages/Models/Sources/Models/List.swift b/Packages/Models/Sources/Models/List.swift index 0f7a5567..4891dec8 100644 --- a/Packages/Models/Sources/Models/List.swift +++ b/Packages/Models/Sources/Models/List.swift @@ -13,7 +13,7 @@ public struct List: Codable, Identifiable, Equatable, Hashable { case followed, list, none } - + public init(id: String, title: String, repliesPolicy: RepliesPolicy? = nil, exclusive: Bool? = nil) { self.id = id self.title = title diff --git a/Packages/Models/Sources/Models/Marker.swift b/Packages/Models/Sources/Models/Marker.swift index e213429e..6f5b9bb0 100644 --- a/Packages/Models/Sources/Models/Marker.swift +++ b/Packages/Models/Sources/Models/Marker.swift @@ -6,7 +6,7 @@ public struct Marker: Codable, Sendable { public let version: Int public let updatedAt: ServerDate } - + public let notifications: Content? public let home: Content? } diff --git a/Packages/Models/Sources/Models/MediaAttachement.swift b/Packages/Models/Sources/Models/MediaAttachement.swift index 69b02d9d..82f098b9 100644 --- a/Packages/Models/Sources/Models/MediaAttachement.swift +++ b/Packages/Models/Sources/Models/MediaAttachement.swift @@ -53,7 +53,7 @@ public struct MediaAttachment: Codable, Identifiable, Hashable, Equatable { description: nil, meta: nil) } - + public static func videoWith(url: URL) -> MediaAttachment { .init(id: UUID().uuidString, type: "video", diff --git a/Packages/Models/Sources/Models/OauthToken.swift b/Packages/Models/Sources/Models/OauthToken.swift index fdbbbd3b..54e0070b 100644 --- a/Packages/Models/Sources/Models/OauthToken.swift +++ b/Packages/Models/Sources/Models/OauthToken.swift @@ -5,7 +5,7 @@ public struct OauthToken: Codable, Hashable, Sendable { public let tokenType: String public let scope: String public let createdAt: Double - + public init(accessToken: String, tokenType: String, scope: String, createdAt: Double) { self.accessToken = accessToken self.tokenType = tokenType diff --git a/Packages/Models/Sources/Models/PostError.swift b/Packages/Models/Sources/Models/PostError.swift index 380301dd..4f036f73 100644 --- a/Packages/Models/Sources/Models/PostError.swift +++ b/Packages/Models/Sources/Models/PostError.swift @@ -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") } + } } diff --git a/Packages/Models/Sources/Models/Status.swift b/Packages/Models/Sources/Models/Status.swift index 172662f8..6edd2f35 100644 --- a/Packages/Models/Sources/Models/Status.swift +++ b/Packages/Models/Sources/Models/Status.swift @@ -73,7 +73,7 @@ public final class Status: AnyStatus, Codable, Identifiable, Equatable, Hashable public let filtered: [Filtered]? public let sensitive: Bool public let language: String? - + public var isHidden: Bool { filtered?.first?.filter.filterAction == .hide } @@ -214,7 +214,7 @@ public final class ReblogStatus: AnyStatus, Codable, Identifiable, Equatable, Ha public let filtered: [Filtered]? public let sensitive: Bool public let language: String? - + public var isHidden: Bool { filtered?.first?.filter.filterAction == .hide } diff --git a/Packages/Models/Sources/Models/Stream/StreamEvent.swift b/Packages/Models/Sources/Models/Stream/StreamEvent.swift index c97d9ebb..1ba79d7d 100644 --- a/Packages/Models/Sources/Models/Stream/StreamEvent.swift +++ b/Packages/Models/Sources/Models/Stream/StreamEvent.swift @@ -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 diff --git a/Packages/Models/Sources/Models/Tag.swift b/Packages/Models/Sources/Models/Tag.swift index 7817281c..676a027d 100644 --- a/Packages/Models/Sources/Models/Tag.swift +++ b/Packages/Models/Sources/Models/Tag.swift @@ -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 { diff --git a/Packages/Network/Sources/Network/Client.swift b/Packages/Network/Sources/Network/Client.swift index b7351a7d..b926f74b 100644 --- a/Packages/Network/Sources/Network/Client.swift +++ b/Packages/Network/Sources/Network/Client.swift @@ -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 { @@ -44,7 +44,7 @@ import OSLog public let version: Version private let urlSession: URLSession private let decoder = JSONDecoder() - + private let logger = Logger(subsystem: "com.icecubesapp", category: "networking") // Putting all mutable state inside an `OSAllocatedUnfairLock` makes `Client` @@ -263,7 +263,7 @@ import OSLog throw error } } - + public func mediaUpload(endpoint: Endpoint, version: Version, method: String, @@ -280,13 +280,14 @@ import OSLog let (_, httpResponse) = try await urlSession.data(for: request) return httpResponse as? HTTPURLResponse } - + private func makeFormDataRequest(endpoint: Endpoint, version: Version, 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 diff --git a/Packages/Network/Sources/Network/Endpoint/Accounts.swift b/Packages/Network/Sources/Network/Endpoint/Accounts.swift index bcc8f8e3..80f66b41 100644 --- a/Packages/Network/Sources/Network/Endpoint/Accounts.swift +++ b/Packages/Network/Sources/Network/Endpoint/Accounts.swift @@ -98,11 +98,11 @@ public enum Accounts: Endpoint { if let sinceId { params.append(.init(name: "max_id", value: sinceId)) } - + params.append(.init(name: "only_media", value: onlyMedia ? "true" : "false")) params.append(.init(name: "exclude_replies", value: excludeReplies ? "true" : "false")) params.append(.init(name: "exclude_reblogs", value: excludeReblogs ? "true" : "false")) - + if let pinned { params.append(.init(name: "pinned", value: pinned ? "true" : "false")) } diff --git a/Packages/Network/Sources/Network/InstanceSocialClient.swift b/Packages/Network/Sources/Network/InstanceSocialClient.swift index 8c2530b3..821bedeb 100644 --- a/Packages/Network/Sources/Network/InstanceSocialClient.swift +++ b/Packages/Network/Sources/Network/InstanceSocialClient.swift @@ -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 diff --git a/Packages/Notifications/Sources/Notifications/NotificationRowView.swift b/Packages/Notifications/Sources/Notifications/NotificationRowView.swift index c3899d73..b512c039 100644 --- a/Packages/Notifications/Sources/Notifications/NotificationRowView.swift +++ b/Packages/Notifications/Sources/Notifications/NotificationRowView.swift @@ -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, diff --git a/Packages/Notifications/Sources/Notifications/NotificationsListView.swift b/Packages/Notifications/Sources/Notifications/NotificationsListView.swift index cbf93cdc..e63afb37 100644 --- a/Packages/Notifications/Sources/Notifications/NotificationsListView.swift +++ b/Packages/Notifications/Sources/Notifications/NotificationsListView.swift @@ -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,17 +170,17 @@ 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) } - + switch nextPageState { case .none: EmptyView() @@ -193,7 +193,7 @@ public struct NotificationsListView: View { bottom: .layoutPadding, trailing: .layoutPadding)) #if !os(visionOS) - .listRowBackground(theme.primaryBackgroundColor) + .listRowBackground(theme.primaryBackgroundColor) #endif } } diff --git a/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift b/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift index 5f44108b..a8c1f971 100644 --- a/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift +++ b/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift @@ -96,7 +96,7 @@ import SwiftUI newNotifications = newNotifications.filter { notification in !consolidatedNotifications.contains(where: { $0.id == notification.id }) } - + await consolidatedNotifications.insert( contentsOf: newNotifications.consolidated(selectedType: selectedType), at: 0 @@ -108,7 +108,7 @@ import SwiftUI } markAsRead() - + withAnimation { state = .display(notifications: consolidatedNotifications, nextPageState: consolidatedNotifications.isEmpty ? .none : nextPageState) @@ -158,13 +158,13 @@ import SwiftUI state = .display(notifications: consolidatedNotifications, nextPageState: newNotifications.count < Constants.notificationLimit ? .none : .hasNextPage) } - + func markAsRead() { guard let client, let id = consolidatedNotifications.first?.notifications.first?.id else { return } Task { do { let _: Marker = try await client.post(endpoint: Markers.markNotifications(lastReadId: id)) - } catch { } + } catch {} } } diff --git a/Packages/StatusKit/Sources/StatusKit/Detail/StatusDetailView.swift b/Packages/StatusKit/Sources/StatusKit/Detail/StatusDetailView.swift index 02d94684..69fc78da 100644 --- a/Packages/StatusKit/Sources/StatusKit/Detail/StatusDetailView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Detail/StatusDetailView.swift @@ -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) diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AIPrompt.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AIPrompt.swift index bd91de0f..b4b94e7f 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AIPrompt.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AIPrompt.swift @@ -37,5 +37,4 @@ extension StatusEditor { } } } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AccessoryView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AccessoryView.swift index 8a3bf0f8..89582e4e 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AccessoryView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AccessoryView.swift @@ -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))) @@ -167,7 +166,7 @@ extension StatusEditor { Image(systemName: "arrowshape.turn.up.left.circle.fill") } .disabled(!canAddNewSEVM) - + if !viewModel.customEmojiContainer.isEmpty { Button { isCustomEmojisSheetDisplay = true @@ -187,20 +186,19 @@ extension StatusEditor { } } } - - + if preferences.isOpenAIEnabled { AIMenu.disabled(!viewModel.canPost) } - + Spacer() - + Button { viewModel.insertStatusText(text: "@") } label: { Image(systemName: "at") } - + Button { viewModel.insertStatusText(text: "#") } label: { @@ -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 { } } } - } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/AutoCompleteView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/AutoCompleteView.swift index 2962495e..62dcd02c 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/AutoCompleteView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/AutoCompleteView.swift @@ -1,28 +1,28 @@ import DesignSystem import EmojiText import Foundation -import SwiftUI import Models import SwiftData +import SwiftUI extension StatusEditor { - @MainActor struct AutoCompleteView: View { @Environment(\.modelContext) var context - + @Environment(Theme.self) var theme - + var viewModel: ViewModel - + @State private var isTagSuggestionExpanded: Bool = false - + @Query(sort: \RecentTag.lastUse, order: .reverse) var recentTags: [RecentTag] 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) { diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/ExpandedView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/ExpandedView.swift index 59f8faa7..20faeea1 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/ExpandedView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/ExpandedView.swift @@ -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 @@ -12,12 +12,12 @@ extension StatusEditor.AutoCompleteView { @Environment(\.modelContext) private var context @Environment(Theme.self) private var theme @Environment(CurrentAccount.self) private var currentAccount - + var viewModel: StatusEditor.ViewModel @Binding var isTagSuggestionExpanded: Bool - + @Query(sort: \RecentTag.lastUse, order: .reverse) var recentTags: [RecentTag] - + var body: some View { TabView { recentTagsPage @@ -26,7 +26,7 @@ extension StatusEditor.AutoCompleteView { .tabViewStyle(.page(indexDisplayMode: .always)) .frame(height: 200) } - + private var recentTagsPage: some View { ScrollView(.vertical) { LazyVStack(alignment: .leading, spacing: 12) { @@ -60,7 +60,7 @@ extension StatusEditor.AutoCompleteView { .padding(.horizontal, .layoutPadding) } } - + private var followedTagsPage: some View { ScrollView(.vertical) { LazyVStack(alignment: .leading, spacing: 12) { diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/MentionsView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/MentionsView.swift index fcff3abe..a3ee1dd7 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/MentionsView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/MentionsView.swift @@ -1,17 +1,16 @@ 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 - + var viewModel: StatusEditor.ViewModel - + var body: some View { ForEach(viewModel.mentionsSuggestions) { account in Button { diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/RecentTagsView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/RecentTagsView.swift index af1d1a3d..0e62a5ed 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/RecentTagsView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/RecentTagsView.swift @@ -1,20 +1,19 @@ 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 - + var viewModel: StatusEditor.ViewModel @Binding var isTagSuggestionExpanded: Bool - + @Query(sort: \RecentTag.lastUse, order: .reverse) var recentTags: [RecentTag] - + var body: some View { ForEach(recentTags) { tag in Button { diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/RemoteTagsView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/RemoteTagsView.swift index 572ef922..bf58412f 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/RemoteTagsView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/AutoComplete/RemoteTagsView.swift @@ -1,21 +1,20 @@ import DesignSystem import EmojiText import Foundation -import SwiftUI import Models import SwiftData - +import SwiftUI extension StatusEditor.AutoCompleteView { struct RemoteTagsView: View { @Environment(\.modelContext) private var context @Environment(Theme.self) private var theme - + var viewModel: StatusEditor.ViewModel @Binding var isTagSuggestionExpanded: Bool - + @Query(sort: \RecentTag.lastUse, order: .reverse) var recentTags: [RecentTag] - + var body: some View { ForEach(viewModel.tagsSuggestions) { tag in Button { diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/CameraPickerView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/CameraPickerView.swift index bcf51c0a..85800b4a 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/CameraPickerView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/CameraPickerView.swift @@ -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) } } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/CategorizedEmojiContainer.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/CategorizedEmojiContainer.swift index a0196636..7bb0fb94 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/CategorizedEmojiContainer.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/CategorizedEmojiContainer.swift @@ -7,5 +7,4 @@ extension StatusEditor { let categoryName: String var emojis: [Emoji] } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/Compressor.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/Compressor.swift index 278d69ff..460804a8 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/Compressor.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/Compressor.swift @@ -2,10 +2,10 @@ 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 { } } } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/CustomEmojisView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/CustomEmojisView.swift index 5c241c16..5ac2ee66 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/CustomEmojisView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/CustomEmojisView.swift @@ -1,17 +1,16 @@ import DesignSystem import Env -import SwiftUI import Models import NukeUI +import SwiftUI extension StatusEditor { - @MainActor struct CustomEmojisView: View { @Environment(\.dismiss) private var dismiss - + @Environment(Theme.self) private var theme - + var viewModel: ViewModel var body: some View { diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/GIF/GIFPickerView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/GIF/GIFPickerView.swift index 61e4ab24..054b0742 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/GIF/GIFPickerView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/GIF/GIFPickerView.swift @@ -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 diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/LangButton.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/LangButton.swift index 935d05b2..23147e47 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/LangButton.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/LangButton.swift @@ -1,19 +1,18 @@ import DesignSystem import Env -import SwiftUI import Models +import SwiftUI extension StatusEditor { - @MainActor struct LangButton: View { @Environment(Theme.self) private var theme @Environment(CurrentInstance.self) private var currentInstance @Environment(UserPreferences.self) private var preferences - + @State private var isLanguageSheetDisplayed: Bool = false @State private var languageSearch: String = "" - + var viewModel: ViewModel var body: some View { @@ -39,7 +38,7 @@ extension StatusEditor { languageSheetView } } - + private var languageSheetView: some View { NavigationStack { List { @@ -68,7 +67,7 @@ extension StatusEditor { .background(theme.secondaryBackgroundColor) } } - + @ViewBuilder private func languageTextView(isoCode: String, nativeName: String?, name: String?) -> some View { if let nativeName, let name { @@ -100,7 +99,7 @@ extension StatusEditor { } } } - + private var recentlyUsedLanguages: [Language] { preferences.recentlyUsedLanguages.compactMap { isoCode in Language.allAvailableLanguages.first { $0.isoCode == isoCode } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/MediaContainer.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/MediaContainer.swift index 02cff14a..3175adff 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/MediaContainer.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/MediaContainer.swift @@ -13,5 +13,4 @@ extension StatusEditor { let mediaAttachment: MediaAttachment? let error: Error? } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/MediaEditView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/MediaEditView.swift index 1f9b9f11..0e68ac28 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/MediaEditView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/MediaEditView.swift @@ -171,5 +171,4 @@ extension StatusEditor { return translation?.content.asRawText } } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/MediaView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/MediaView.swift index 96dfab5c..3e08fa2a 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/MediaView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/MediaView.swift @@ -249,5 +249,4 @@ extension StatusEditor { .cornerRadius(8) } } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/PollView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/PollView.swift index 08fc5a0c..846e6264 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/PollView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/PollView.swift @@ -121,5 +121,4 @@ extension StatusEditor { return index == count - 1 && count < maxEntries } } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/UTTypeSupported.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/UTTypeSupported.swift index 7e95ebc3..fc158a3c 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/UTTypeSupported.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/UTTypeSupported.swift @@ -9,7 +9,7 @@ extension StatusEditor { @MainActor struct UTTypeSupported { let value: String - + func loadItemContent(item: NSItemProvider) async throws -> Any? { if let transferable = await getVideoTransferable(item: item) { return transferable @@ -79,21 +79,21 @@ extension StatusEditor { self.url = url _ = url.startAccessingSecurityScopedResource() } - + deinit { url.stopAccessingSecurityScopedResource() } - + 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) } } } - + final class GifFileTranseferable: Transferable, Sendable { let url: URL @@ -101,18 +101,18 @@ extension StatusEditor { self.url = url _ = url.startAccessingSecurityScopedResource() } - + deinit { url.stopAccessingSecurityScopedResource() } - + var data: Data? { try? Data(contentsOf: url) } static var transferRepresentation: some TransferRepresentation { FileRepresentation(importedContentType: .gif) { receivedTransferrable in - return GifFileTranseferable(url: receivedTransferrable.localURL) + GifFileTranseferable(url: receivedTransferrable.localURL) } } } @@ -121,19 +121,19 @@ extension StatusEditor { public extension StatusEditor { final class ImageFileTranseferable: Transferable, Sendable { public let url: URL - + init(url: URL) { self.url = url _ = url.startAccessingSecurityScopedResource() } - + deinit { url.stopAccessingSecurityScopedResource() } 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 { diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Drafts/DraftsListView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Drafts/DraftsListView.swift index 026bbadd..b427ed3a 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Drafts/DraftsListView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Drafts/DraftsListView.swift @@ -49,5 +49,4 @@ extension StatusEditor { } } } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/EditorFocusState.swift b/Packages/StatusKit/Sources/StatusKit/Editor/EditorFocusState.swift index 0051a890..0b644c30 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/EditorFocusState.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/EditorFocusState.swift @@ -4,5 +4,4 @@ extension StatusEditor { enum EditorFocusState: Hashable { case main, followUp(index: UUID) } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/EditorView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/EditorView.swift index 02af138d..a445cc61 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/EditorView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/EditorView.swift @@ -19,7 +19,7 @@ extension StatusEditor { #else @Environment(\.dismiss) private var dismiss #endif - + @Bindable var viewModel: ViewModel @Binding var followUpSEVMs: [ViewModel] @Binding var editingMediaContainer: MediaContainer? @@ -137,13 +137,13 @@ extension StatusEditor { .environment(\.isModal, true) .padding(.horizontal, .layoutPadding) .padding(.vertical, .statusComponentSpacing) - #if os(visionOS) + #if os(visionOS) .background(RoundedRectangle(cornerRadius: 8) .foregroundStyle(.background)) .buttonStyle(.plain) .padding(.layoutPadding) - #endif - + #endif + } else if let status = viewModel.embeddedStatus { StatusEmbeddedView(status: status, client: client, routerPath: RouterPath()) .padding(.horizontal, .layoutPadding) @@ -158,15 +158,14 @@ extension StatusEditor { .padding(.horizontal) } } - - + @ViewBuilder private var characterCountAndLangView: some View { let value = (currentInstance.instance?.configuration?.statuses.maxCharacters ?? 500) + viewModel.statusTextCharacterLength HStack(alignment: .center) { LangButton(viewModel: viewModel) .padding(.leading, .layoutPadding) - + Button { withAnimation { viewModel.showPoll.toggle() @@ -189,9 +188,9 @@ extension StatusEditor { } .buttonStyle(.bordered) .accessibilityLabel("accessibility.editor.button.spoiler") - + Spacer() - + Text("\(value)") .foregroundColor(value < 0 ? .red : .secondary) .font(.callout.monospacedDigit()) @@ -204,7 +203,7 @@ extension StatusEditor { } .padding(.vertical, 8) } - + private func setupViewModel() { viewModel.client = client viewModel.currentAccount = currentAccount.account @@ -223,5 +222,4 @@ extension StatusEditor { Task { await viewModel.fetchCustomEmojis() } } } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/MainView.swift b/Packages/StatusKit/Sources/StatusKit/Editor/MainView.swift index 01468738..c0b667d2 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/MainView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/MainView.swift @@ -10,9 +10,9 @@ import StoreKit import SwiftUI import UIKit -extension StatusEditor { +public extension StatusEditor { @MainActor - public struct MainView: View { + struct MainView: View { @Environment(AppAccountsManager.self) private var appAccounts @Environment(CurrentAccount.self) private var currentAccount @Environment(Theme.self) private var theme @@ -48,7 +48,7 @@ extension StatusEditor { } EditorView( viewModel: mainSEVM, - followUpSEVMs: $followUpSEVMs, + followUpSEVMs: $followUpSEVMs, editingMediaContainer: $editingMediaContainer, editorFocusState: $editorFocusState, assignedFocusState: .main, @@ -76,73 +76,73 @@ extension StatusEditor { .animation(.bouncy(duration: 0.3), value: editorFocusState) .animation(.bouncy(duration: 0.3), value: followUpSEVMs) #if !os(visionOS) - .background(theme.primaryBackgroundColor) + .background(theme.primaryBackgroundColor) #endif - .safeAreaInset(edge: .bottom) { - AutoCompleteView(viewModel: focusedSEVM) - } + .safeAreaInset(edge: .bottom) { + AutoCompleteView(viewModel: focusedSEVM) + } #if os(visionOS) - .ornament(attachmentAnchor: .scene(.leading)) { - AccessoryView(focusedSEVM: focusedSEVM, - followUpSEVMs: $followUpSEVMs) - } - #else - .safeAreaInset(edge: .bottom) { - if presentationDetent == .large || presentationDetent == .medium { + .ornament(attachmentAnchor: .scene(.leading)) { AccessoryView(focusedSEVM: focusedSEVM, followUpSEVMs: $followUpSEVMs) } - } - #endif - .accessibilitySortPriority(1) // Ensure that all elements inside the `ScrollView` occur earlier than the accessory views - .navigationTitle(focusedSEVM.mode.title) - .navigationBarTitleDisplayMode(.inline) - .toolbar { ToolbarItems(mainSEVM: mainSEVM, - focusedSEVM: focusedSEVM, - followUpSEVMs: followUpSEVMs) } - .toolbarBackground(.visible, for: .navigationBar) - .alert( - "status.error.posting.title", - isPresented: $focusedSEVM.showPostingErrorAlert, - actions: { - Button("OK") {} - }, message: { - Text(mainSEVM.postingError ?? "") - } - ) - .interactiveDismissDisabled(mainSEVM.shouldDisplayDismissWarning) - .onChange(of: appAccounts.currentClient) { _, newValue in - if mainSEVM.mode.isInShareExtension { - currentAccount.setClient(client: newValue) - mainSEVM.client = newValue - for post in followUpSEVMs { - post.client = newValue - } - } - } - .onDrop(of: [.image, .video, .gif, .mpeg4Movie, .quickTimeMovie, .movie], - delegate: focusedSEVM) - .onChange(of: currentAccount.account?.id) { - mainSEVM.currentAccount = currentAccount.account - for p in followUpSEVMs { - p.currentAccount = mainSEVM.currentAccount - } - } - .onChange(of: mainSEVM.visibility) { - for p in followUpSEVMs { - p.visibility = mainSEVM.visibility - } - } - .onChange(of: followUpSEVMs.count) { oldValue, newValue in - if oldValue < newValue { - Task { - try? await Task.sleep(for: .seconds(0.1)) - withAnimation(.bouncy(duration: 0.5)) { - scrollID = followUpSEVMs.last?.id + #else + .safeAreaInset(edge: .bottom) { + if presentationDetent == .large || presentationDetent == .medium { + AccessoryView(focusedSEVM: focusedSEVM, + followUpSEVMs: $followUpSEVMs) + } + } + #endif + .accessibilitySortPriority(1) // Ensure that all elements inside the `ScrollView` occur earlier than the accessory views + .navigationTitle(focusedSEVM.mode.title) + .navigationBarTitleDisplayMode(.inline) + .toolbar { ToolbarItems(mainSEVM: mainSEVM, + focusedSEVM: focusedSEVM, + followUpSEVMs: followUpSEVMs) } + .toolbarBackground(.visible, for: .navigationBar) + .alert( + "status.error.posting.title", + isPresented: $focusedSEVM.showPostingErrorAlert, + actions: { + Button("OK") {} + }, message: { + Text(mainSEVM.postingError ?? "") + } + ) + .interactiveDismissDisabled(mainSEVM.shouldDisplayDismissWarning) + .onChange(of: appAccounts.currentClient) { _, newValue in + if mainSEVM.mode.isInShareExtension { + currentAccount.setClient(client: newValue) + mainSEVM.client = newValue + for post in followUpSEVMs { + post.client = newValue + } + } + } + .onDrop(of: [.image, .video, .gif, .mpeg4Movie, .quickTimeMovie, .movie], + delegate: focusedSEVM) + .onChange(of: currentAccount.account?.id) { + mainSEVM.currentAccount = currentAccount.account + for p in followUpSEVMs { + p.currentAccount = mainSEVM.currentAccount + } + } + .onChange(of: mainSEVM.visibility) { + for p in followUpSEVMs { + p.visibility = mainSEVM.visibility + } + } + .onChange(of: followUpSEVMs.count) { oldValue, newValue in + if oldValue < newValue { + Task { + try? await Task.sleep(for: .seconds(0.1)) + withAnimation(.bouncy(duration: 0.5)) { + scrollID = followUpSEVMs.last?.id + } + } } } - } - } } .sheet(item: $editingMediaContainer) { container in StatusEditor.MediaEditView(viewModel: focusedSEVM, container: container) @@ -151,5 +151,4 @@ extension StatusEditor { .presentationBackgroundInteraction(.enabled) } } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Namespace.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Namespace.swift index 9a8f7efe..7ba8fb8e 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Namespace.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Namespace.swift @@ -1 +1 @@ -public enum StatusEditor { } +public enum StatusEditor {} diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/PrivacyMenu.swift b/Packages/StatusKit/Sources/StatusKit/Editor/PrivacyMenu.swift index 27d15554..e431393b 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/PrivacyMenu.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/PrivacyMenu.swift @@ -30,5 +30,4 @@ extension StatusEditor { } } } - } diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/ToolbarItems.swift b/Packages/StatusKit/Sources/StatusKit/Editor/ToolbarItems.swift index 6632d31f..c19faae3 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/ToolbarItems.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/ToolbarItems.swift @@ -1,8 +1,8 @@ +import DesignSystem import Env import Models import StoreKit import SwiftUI -import DesignSystem extension StatusEditor { @MainActor @@ -10,7 +10,7 @@ extension StatusEditor { @State private var isLanguageConfirmPresented = false @State private var isDismissAlertPresented: Bool = false @State private var isDraftsSheetDisplayed: Bool = false - + let mainSEVM: ViewModel let focusedSEVM: ViewModel let followUpSEVMs: [ViewModel] @@ -18,7 +18,7 @@ extension StatusEditor { @Environment(\.modelContext) private var context @Environment(UserPreferences.self) private var preferences @Environment(Theme.self) private var theme - + #if targetEnvironment(macCatalyst) @Environment(\.dismissWindow) private var dismissWindow #else @@ -45,7 +45,7 @@ extension StatusEditor { } } } - + ToolbarItem(placement: .navigationBarTrailing) { Button { Task { @@ -160,7 +160,7 @@ extension StatusEditor { EmptyView() } } - + private var draftsListView: some View { DraftsListView(selectedDraft: .init(get: { nil diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/ViewModel.swift b/Packages/StatusKit/Sources/StatusKit/Editor/ViewModel.swift index e8a71a04..f91536bc 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/ViewModel.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/ViewModel.swift @@ -7,10 +7,9 @@ import Network import PhotosUI import SwiftUI -extension StatusEditor { - +public extension StatusEditor { @MainActor - @Observable public class ViewModel: NSObject, Identifiable { + @Observable class ViewModel: NSObject, Identifiable { public let id = UUID() var mode: Mode @@ -90,7 +89,7 @@ extension StatusEditor { var postingProgress: Double = 0.0 var postingTimer: Timer? var isPosting: Bool = false - + var mediaPickers: [PhotosPickerItem] = [] { didSet { if mediaPickers.count > 4 { @@ -130,17 +129,18 @@ extension StatusEditor { var shouldDisablePollButton: Bool { !mediaContainers.isEmpty } - + var allMediaHasDescription: Bool { - var everyMediaHasAltText: Bool = true; - mediaContainers.forEach { mediaContainer in - if (((mediaContainer.mediaAttachment?.description) == nil) || - mediaContainer.mediaAttachment?.description?.count == 0) { - everyMediaHasAltText = false - } + var everyMediaHasAltText = true + for mediaContainer in mediaContainers { + if ((mediaContainer.mediaAttachment?.description) == nil) || + mediaContainer.mediaAttachment?.description?.count == 0 + { + everyMediaHasAltText = false } - - return everyMediaHasAltText; + } + + return everyMediaHasAltText } var shouldDisplayDismissWarning: Bool { @@ -200,12 +200,12 @@ extension StatusEditor { func postStatus() async -> Status? { guard let client else { return nil } do { - if (!allMediaHasDescription && UserPreferences.shared.appRequireAltText) { + if !allMediaHasDescription && UserPreferences.shared.appRequireAltText { throw PostError.missingAltText } - + if postingTimer == nil { - Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { timer in + Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { _ in Task { @MainActor in if self.postingProgress < 100 { self.postingProgress += 0.5 @@ -245,21 +245,21 @@ extension StatusEditor { StreamWatcher.shared.emmitEditEvent(for: postStatus) } } - + postingTimer?.invalidate() postingTimer = nil - + withAnimation { postingProgress = 99.0 } try await Task.sleep(for: .seconds(0.5)) HapticManager.shared.fireHaptic(.notification(.success)) - + if hasExplicitlySelectedLanguage, let selectedLanguage { preferences?.markLanguageAsSelected(isoCode: selectedLanguage) } isPosting = false - + return postStatus } catch { if let error = error as? Models.ServerError { @@ -267,8 +267,8 @@ extension StatusEditor { showPostingErrorAlert = true } if let postError = error as? PostError { - postingError = postError.description - showPostingErrorAlert = true + postingError = postError.description + showPostingErrorAlert = true } isPosting = false HapticManager.shared.fireHaptic(.notification(.error)) @@ -614,9 +614,10 @@ extension StatusEditor { private func resetAutoCompletion() { if !tagsSuggestions.isEmpty || - !mentionsSuggestions.isEmpty || - currentSuggestionRange != nil || - showRecentsTagsInline { + !mentionsSuggestions.isEmpty || + currentSuggestionRange != nil || + showRecentsTagsInline + { withAnimation { tagsSuggestions = [] mentionsSuggestions = [] @@ -838,7 +839,7 @@ extension StatusEditor { error: nil ) } - } catch { } + } catch {} } try? await Task.sleep(for: .seconds(5)) } while !Task.isCancelled @@ -859,7 +860,7 @@ extension StatusEditor { mediaAttachment: media, error: nil ) - } catch { } + } catch {} } } diff --git a/Packages/StatusKit/Sources/StatusKit/Embed/StatusEmbeddedView.swift b/Packages/StatusKit/Sources/StatusKit/Embed/StatusEmbeddedView.swift index 20312b3b..df96a64b 100644 --- a/Packages/StatusKit/Sources/StatusKit/Embed/StatusEmbeddedView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Embed/StatusEmbeddedView.swift @@ -36,17 +36,17 @@ public struct StatusEmbeddedView: View { } .padding(8) #if os(visionOS) - .background(Material.thickMaterial) + .background(Material.thickMaterial) #else - .background(theme.secondaryBackgroundColor) + .background(theme.secondaryBackgroundColor) #endif - .cornerRadius(4) - .overlay( - RoundedRectangle(cornerRadius: 4) - .stroke(.gray.opacity(0.35), lineWidth: 1) - ) - .padding(.top, 8) - .accessibilityElement(children: .combine) + .cornerRadius(4) + .overlay( + RoundedRectangle(cornerRadius: 4) + .stroke(.gray.opacity(0.35), lineWidth: 1) + ) + .padding(.top, 8) + .accessibilityElement(children: .combine) } private func makeAccountView(account: Account) -> some View { diff --git a/Packages/StatusKit/Sources/StatusKit/History/StatusEditHistoryView.swift b/Packages/StatusKit/Sources/StatusKit/History/StatusEditHistoryView.swift index 544b3012..2dce38e2 100644 --- a/Packages/StatusKit/Sources/StatusKit/History/StatusEditHistoryView.swift +++ b/Packages/StatusKit/Sources/StatusKit/History/StatusEditHistoryView.swift @@ -56,7 +56,7 @@ public struct StatusEditHistoryView: View { .task { do { history = try await client.get(endpoint: Statuses.history(id: statusId)) - } catch { } + } catch {} } .listStyle(.plain) .scrollContentBackground(.hidden) diff --git a/Packages/StatusKit/Sources/StatusKit/LanguageDetection/LanguageDetection.swift b/Packages/StatusKit/Sources/StatusKit/LanguageDetection/LanguageDetection.swift index c95cf31a..8e547d5a 100644 --- a/Packages/StatusKit/Sources/StatusKit/LanguageDetection/LanguageDetection.swift +++ b/Packages/StatusKit/Sources/StatusKit/LanguageDetection/LanguageDetection.swift @@ -8,7 +8,7 @@ private func stripToPureLanguage(inText: String) -> String { var resultStr = inText - [hashtagRegex, emojiRegex, atRegex].forEach { regex in + for regex in [hashtagRegex, emojiRegex, atRegex] { let splitArray = resultStr.split(separator: regex, omittingEmptySubsequences: true) resultStr = splitArray.joined() as String } diff --git a/Packages/StatusKit/Sources/StatusKit/List/StatusesListView.swift b/Packages/StatusKit/Sources/StatusKit/List/StatusesListView.swift index 68f301ce..abe77e18 100644 --- a/Packages/StatusKit/Sources/StatusKit/List/StatusesListView.swift +++ b/Packages/StatusKit/Sources/StatusKit/List/StatusesListView.swift @@ -65,9 +65,9 @@ public struct StatusesListView: View where Fetcher: StatusesFetcher { } .padding(.horizontal, .layoutPadding) #if !os(visionOS) - .listRowBackground(theme.primaryBackgroundColor) + .listRowBackground(theme.primaryBackgroundColor) #endif - + case .none: EmptyView() } diff --git a/Packages/StatusKit/Sources/StatusKit/Poll/StatusPollView.swift b/Packages/StatusKit/Sources/StatusKit/Poll/StatusPollView.swift index 16a34945..b2379864 100644 --- a/Packages/StatusKit/Sources/StatusKit/Poll/StatusPollView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Poll/StatusPollView.swift @@ -78,7 +78,7 @@ public struct StatusPollView: View { // Make sure they're all the same width using a ZStack with 100% hiding behind the // real percentage. Text("100%").hidden().overlay(alignment: .trailing) { - Text("\(absolutePercent(for:option.votesCount ?? 0))%") + Text("\(absolutePercent(for: option.votesCount ?? 0))%") .font(.scaledSubheadline) } } @@ -119,9 +119,9 @@ public struct StatusPollView: View { func combinedAccessibilityLabel(for option: Poll.Option, index: Int) -> Text { let showPercentage = viewModel.poll.expired || viewModel.poll.voted ?? false return Text("accessibility.status.poll.option-prefix-\(index + 1)-of-\(viewModel.poll.options.count)") + - Text(", ") + - Text(option.title) + - Text(showPercentage ? ", \(absolutePercent(for:option.votesCount ?? 0))%" : "") + Text(", ") + + Text(option.title) + + Text(showPercentage ? ", \(absolutePercent(for: option.votesCount ?? 0))%" : "") } private var footerView: some View { @@ -188,19 +188,20 @@ public struct StatusPollView: View { private struct _PercentWidthLayout: Layout { let percent: CGFloat - func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { + func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) -> CGSize { guard let view = subviews.first else { return CGSize.zero } return view.sizeThatFits(proposal) } - func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { + func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) { guard let view = subviews.first, let width = proposal.width else { return } view.place( at: bounds.origin, - proposal: ProposedViewSize(width: percent / 100 * width, height: proposal.height)) + proposal: ProposedViewSize(width: percent / 100 * width, height: proposal.height) + ) } } } diff --git a/Packages/StatusKit/Sources/StatusKit/Poll/StatusPollViewModel.swift b/Packages/StatusKit/Sources/StatusKit/Poll/StatusPollViewModel.swift index cf6c0568..c60e8e2e 100644 --- a/Packages/StatusKit/Sources/StatusKit/Poll/StatusPollViewModel.swift +++ b/Packages/StatusKit/Sources/StatusKit/Poll/StatusPollViewModel.swift @@ -35,7 +35,7 @@ import SwiftUI votes = poll.ownVotes ?? [] showResults = true } - } catch { } + } catch {} } public func handleSelection(_ pollIndex: Int) { diff --git a/Packages/StatusKit/Sources/StatusKit/Row/StatusRowAccessibilityLabel.swift b/Packages/StatusKit/Sources/StatusKit/Row/StatusRowAccessibilityLabel.swift index de8a0c76..116e4dec 100644 --- a/Packages/StatusKit/Sources/StatusKit/Row/StatusRowAccessibilityLabel.swift +++ b/Packages/StatusKit/Sources/StatusKit/Row/StatusRowAccessibilityLabel.swift @@ -1,5 +1,5 @@ -import SwiftUI import Models +import SwiftUI /// A utility that creates a suitable combined accessibility label for a `StatusRowView` that is not focused. @MainActor diff --git a/Packages/StatusKit/Sources/StatusKit/Row/StatusRowView.swift b/Packages/StatusKit/Sources/StatusKit/Row/StatusRowView.swift index e23d9db8..6c45b54d 100644 --- a/Packages/StatusKit/Sources/StatusKit/Row/StatusRowView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Row/StatusRowView.swift @@ -28,12 +28,12 @@ public struct StatusRowView: View { private let context: Context public init(viewModel: StatusRowViewModel, context: Context = .timeline) { - self._viewModel = .init(initialValue: viewModel) + _viewModel = .init(initialValue: viewModel) self.context = context } var contextMenu: some View { - StatusRowContextMenu(viewModel: viewModel, + StatusRowContextMenu(viewModel: viewModel, showTextForSelection: $showSelectableText, isBlockConfirmationPresented: $isBlockConfirmationPresented) } @@ -99,8 +99,9 @@ public struct StatusRowView: View { } } if !reasons.contains(.placeholder), - viewModel.showActions, isFocused || theme.statusActionsDisplay != .none, - !isInCaptureMode { + viewModel.showActions, isFocused || theme.statusActionsDisplay != .none, + !isInCaptureMode + { StatusRowActionsView(isBlockConfirmationPresented: $isBlockConfirmationPresented, viewModel: viewModel) .tint(isFocused ? theme.tintColor : .gray) @@ -196,13 +197,14 @@ public struct StatusRowView: View { ) }) .confirmationDialog("", - isPresented: $isBlockConfirmationPresented) { + isPresented: $isBlockConfirmationPresented) + { Button("account.action.block", role: .destructive) { Task { do { let operationAccount = viewModel.status.reblog?.account ?? viewModel.status.account viewModel.authorRelationship = try await client.post(endpoint: Accounts.block(id: operationAccount.id)) - } catch { } + } catch {} } } } @@ -334,20 +336,20 @@ public struct StatusRowView: View { #Preview { List { StatusRowView(viewModel: - .init(status: .placeholder(), - client: .init(server: ""), - routerPath: RouterPath()), - context: .timeline) + .init(status: .placeholder(), + client: .init(server: ""), + routerPath: RouterPath()), + context: .timeline) StatusRowView(viewModel: - .init(status: .placeholder(), - client: .init(server: ""), - routerPath: RouterPath()), - context: .timeline) + .init(status: .placeholder(), + client: .init(server: ""), + routerPath: RouterPath()), + context: .timeline) StatusRowView(viewModel: - .init(status: .placeholder(), - client: .init(server: ""), - routerPath: RouterPath()), - context: .timeline) + .init(status: .placeholder(), + client: .init(server: ""), + routerPath: RouterPath()), + context: .timeline) }.listStyle(.plain) .environment(RouterPath()) .environment(Client(server: "")) @@ -358,4 +360,3 @@ public struct StatusRowView: View { .environment(PushNotificationsService.shared) .environment(QuickLook.shared) } - diff --git a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowActionsView.swift b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowActionsView.swift index 842120f2..f686a5b3 100644 --- a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowActionsView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowActionsView.swift @@ -14,13 +14,13 @@ struct StatusRowActionsView: View { @Environment(\.openWindow) private var openWindow @Environment(\.isStatusFocused) private var isFocused @Environment(\.horizontalSizeClass) var horizontalSizeClass - + @State private var showTextForSelection: Bool = false - + @Binding var isBlockConfirmationPresented: Bool var viewModel: StatusRowViewModel - + var isNarrow: Bool { horizontalSizeClass == .compact && (UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac) } @@ -28,7 +28,7 @@ struct StatusRowActionsView: View { func privateBoost() -> Bool { viewModel.status.visibility == .priv && viewModel.status.account.id == currentAccount.account?.id } - + var actions: [Action] { switch theme.statusActionSecondary { case .share: @@ -147,19 +147,19 @@ struct StatusRowActionsView: View { .padding(.vertical, 6) .padding(.horizontal, 8) .contentShape(Rectangle()) - #if targetEnvironment(macCatalyst) + #if targetEnvironment(macCatalyst) .font(.scaledBody) - #else + #else .font(.body) .dynamicTypeSize(.large) - #endif + #endif } .buttonStyle(.borderless) #if !os(visionOS) - .offset(x: -8) + .offset(x: -8) #endif - .accessibilityElement(children: .combine) - .accessibilityLabel("status.action.share-link") + .accessibilityElement(children: .combine) + .accessibilityLabel("status.action.share-link") case .linkAndText: ShareLink(item: url, subject: Text(viewModel.finalStatus.account.safeDisplayName), @@ -170,25 +170,25 @@ struct StatusRowActionsView: View { .padding(.vertical, 6) .padding(.horizontal, 8) .contentShape(Rectangle()) - #if targetEnvironment(macCatalyst) + #if targetEnvironment(macCatalyst) .font(.scaledBody) - #else + #else .font(.body) .dynamicTypeSize(.large) - #endif + #endif } .buttonStyle(.borderless) #if !os(visionOS) - .offset(x: -8) + .offset(x: -8) #endif - .accessibilityElement(children: .combine) - .accessibilityLabel("status.action.share-link") + .accessibilityElement(children: .combine) + .accessibilityLabel("status.action.share-link") } } Spacer() } else if action == .menu { Menu { - StatusRowContextMenu(viewModel: viewModel, + StatusRowContextMenu(viewModel: viewModel, showTextForSelection: $showTextForSelection, isBlockConfirmationPresented: $isBlockConfirmationPresented) .onAppear { @@ -228,21 +228,21 @@ struct StatusRowActionsView: View { .image(dataController: statusDataController, privateBoost: privateBoost()) .imageScale(.medium) .fontWeight(.black) - #if targetEnvironment(macCatalyst) + #if targetEnvironment(macCatalyst) .font(.scaledBody) - #else + #else .font(.body) .dynamicTypeSize(.large) - #endif + #endif } else { action .image(dataController: statusDataController, privateBoost: privateBoost()) - #if targetEnvironment(macCatalyst) + #if targetEnvironment(macCatalyst) .font(.scaledBody) - #else + #else .font(.body) .dynamicTypeSize(.large) - #endif + #endif } if !isNarrow, let count = action.count(dataController: statusDataController, @@ -254,12 +254,12 @@ struct StatusRowActionsView: View { .minimumScaleFactor(0.6) .contentTransition(.numericText(value: Double(count))) .foregroundColor(Color(UIColor.secondaryLabel)) - #if targetEnvironment(macCatalyst) + #if targetEnvironment(macCatalyst) .font(.scaledFootnote) - #else + #else .font(.footnote) .dynamicTypeSize(.medium) - #endif + #endif .monospacedDigit() .opacity(count > 0 ? 1 : 0) } @@ -273,15 +273,15 @@ struct StatusRowActionsView: View { .foregroundColor(Color(UIColor.secondaryLabel)) #else .buttonStyle( - .statusAction( - isOn: action.isOn(dataController: statusDataController), - tintColor: action.tintColor(theme: theme) - ) + .statusAction( + isOn: action.isOn(dataController: statusDataController), + tintColor: action.tintColor(theme: theme) + ) ) .offset(x: -8) #endif .disabled(action == .boost && - (viewModel.status.visibility == .direct || viewModel.status.visibility == .priv && viewModel.status.account.id != currentAccount.account?.id)) + (viewModel.status.visibility == .direct || viewModel.status.visibility == .priv && viewModel.status.account.id != currentAccount.account?.id)) .accessibilityElement(children: .combine) .accessibilityLabel(action.accessibilityLabel(dataController: statusDataController, privateBoost: privateBoost())) } diff --git a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowCardView.swift b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowCardView.swift index f1965999..23f8c752 100644 --- a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowCardView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowCardView.swift @@ -1,9 +1,9 @@ import DesignSystem +import Env import Models import Nuke import NukeUI import SwiftUI -import Env @MainActor public struct StatusRowCardView: View { @@ -53,10 +53,11 @@ public struct StatusRowCardView: View { let sitesWithIcons = ["apps.apple.com", "music.apple.com", "podcasts.apple.com", "open.spotify.com"] if isCompact { compactLinkPreview(title, url) - } else if (UIDevice.current.userInterfaceIdiom == .pad || - UIDevice.current.userInterfaceIdiom == .mac || - UIDevice.current.userInterfaceIdiom == .vision), - let host = url.host(), sitesWithIcons.contains(host) { + } else if UIDevice.current.userInterfaceIdiom == .pad || + UIDevice.current.userInterfaceIdiom == .mac || + UIDevice.current.userInterfaceIdiom == .vision, + let host = url.host(), sitesWithIcons.contains(host) + { iconLinkPreview(title, url) } else { defaultLinkPreview(title, url) @@ -65,37 +66,37 @@ public struct StatusRowCardView: View { .frame(maxWidth: maxWidth) .fixedSize(horizontal: false, vertical: true) #if os(visionOS) - .if(!isCompact, transform: { view in - view.background(.background) - }) - .hoverEffect() + .if(!isCompact, transform: { view in + view.background(.background) + }) + .hoverEffect() #else - .background(isCompact ? .clear : theme.secondaryBackgroundColor) + .background(isCompact ? .clear : theme.secondaryBackgroundColor) #endif - .cornerRadius(isCompact ? 0 : 10) - .overlay { - if !isCompact { - RoundedRectangle(cornerRadius: 10) - .stroke(.gray.opacity(0.35), lineWidth: 1) + .cornerRadius(isCompact ? 0 : 10) + .overlay { + if !isCompact { + RoundedRectangle(cornerRadius: 10) + .stroke(.gray.opacity(0.35), lineWidth: 1) + } } - } - .contextMenu { - ShareLink(item: url) { - Label("status.card.share", systemImage: "square.and.arrow.up") + .contextMenu { + ShareLink(item: url) { + Label("status.card.share", systemImage: "square.and.arrow.up") + } + Button { openURL(url) } label: { + Label("status.action.view-in-browser", systemImage: "safari") + } + Divider() + Button { + UIPasteboard.general.url = url + } label: { + Label("status.card.copy", systemImage: "doc.on.doc") + } } - Button { openURL(url) } label: { - Label("status.action.view-in-browser", systemImage: "safari") - } - Divider() - Button { - UIPasteboard.general.url = url - } label: { - Label("status.card.copy", systemImage: "doc.on.doc") - } - } - .accessibilityElement(children: .combine) - .accessibilityAddTraits(.isLink) - .accessibilityRemoveTraits(.isStaticText) + .accessibilityElement(children: .combine) + .accessibilityAddTraits(.isLink) + .accessibilityRemoveTraits(.isStaticText) } } .buttonStyle(.plain) @@ -125,7 +126,7 @@ public struct StatusRowCardView: View { .frame(maxWidth: .infinity, alignment: .leading) .padding(10) } - + private func compactLinkPreview(_ title: String, _ url: URL) -> some View { HStack(alignment: .top) { if let imageURL = card.image, !isInCaptureMode { @@ -156,16 +157,16 @@ public struct StatusRowCardView: View { .foregroundColor(theme.tintColor) .lineLimit(1) if let history = card.history { - let uses = history.compactMap{ Int($0.accounts )}.reduce(0, +) + let uses = history.compactMap { Int($0.accounts) }.reduce(0, +) HStack(spacing: 4) { Image(systemName: "bubble.left.and.text.bubble.right") Text("trending-tag-people-talking \(uses)") Spacer() Button { #if targetEnvironment(macCatalyst) - openWindow(value: WindowDestinationEditor.quoteLinkStatusEditor(link: url)) + openWindow(value: WindowDestinationEditor.quoteLinkStatusEditor(link: url)) #else - routerPath.presentedSheet = .quoteLinkStatusEditor(link: url) + routerPath.presentedSheet = .quoteLinkStatusEditor(link: url) #endif } label: { Image(systemName: "quote.opening") @@ -225,7 +226,7 @@ public struct StatusRowCardView: View { struct DefaultPreviewImage: View { @Environment(Theme.self) private var theme - + let url: URL let originalWidth: CGFloat let originalHeight: CGFloat @@ -250,12 +251,12 @@ struct DefaultPreviewImage: View { let originalWidth: CGFloat let originalHeight: CGFloat - func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { + func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) -> CGSize { guard !subviews.isEmpty else { return CGSize.zero } return calculateSize(proposal) } - func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { + func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) { guard let view = subviews.first else { return } let size = calculateSize(proposal) diff --git a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowContextMenu.swift b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowContextMenu.swift index 7dd9d11f..16358bbb 100644 --- a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowContextMenu.swift +++ b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowContextMenu.swift @@ -204,7 +204,7 @@ struct StatusRowContextMenu: View { do { let operationAccount = viewModel.status.reblog?.account ?? viewModel.status.account viewModel.authorRelationship = try await client.post(endpoint: Accounts.unmute(id: operationAccount.id)) - } catch { } + } catch {} } } label: { Label("account.action.unmute", systemImage: "speaker") @@ -217,7 +217,7 @@ struct StatusRowContextMenu: View { do { let operationAccount = viewModel.status.reblog?.account ?? viewModel.status.account viewModel.authorRelationship = try await client.post(endpoint: Accounts.mute(id: operationAccount.id, json: MuteData(duration: duration.rawValue))) - } catch { } + } catch {} } } } @@ -225,13 +225,13 @@ struct StatusRowContextMenu: View { Label("account.action.mute", systemImage: "speaker.slash") } } - + #if targetEnvironment(macCatalyst) accountContactMenuItems #else - ControlGroup { - accountContactMenuItems - } + ControlGroup { + accountContactMenuItems + } #endif } } @@ -244,7 +244,7 @@ struct StatusRowContextMenu: View { } } } - + @ViewBuilder private var accountContactMenuItems: some View { Button { @@ -271,7 +271,7 @@ struct StatusRowContextMenu: View { do { let operationAccount = viewModel.status.reblog?.account ?? viewModel.status.account viewModel.authorRelationship = try await client.post(endpoint: Accounts.unblock(id: operationAccount.id)) - } catch { } + } catch {} } } label: { Label("account.action.unblock", systemImage: "person.crop.circle.badge.exclamationmark") diff --git a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowHeaderView.swift b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowHeaderView.swift index 8395e958..98cea12f 100644 --- a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowHeaderView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowHeaderView.swift @@ -1,8 +1,8 @@ import DesignSystem import Env import Models -import SwiftUI import Network +import SwiftUI @MainActor struct StatusRowHeaderView: View { @@ -38,9 +38,9 @@ struct StatusRowHeaderView: View { HStack(alignment: .center) { if theme.avatarPosition == .top { AvatarView(viewModel.finalStatus.account.avatar) - #if targetEnvironment(macCatalyst) + #if targetEnvironment(macCatalyst) .accountPopover(viewModel.finalStatus.account) - #endif + #endif } VStack(alignment: .leading, spacing: 2) { HStack(alignment: .firstTextBaseline, spacing: 2) { @@ -53,9 +53,9 @@ struct StatusRowHeaderView: View { .emojiText.baselineOffset(Font.scaledSubheadlineFont.emojiBaselineOffset) .fontWeight(.semibold) .lineLimit(1) - #if targetEnvironment(macCatalyst) + #if targetEnvironment(macCatalyst) .accountPopover(viewModel.finalStatus.account) - #endif + #endif if !redactionReasons.contains(.placeholder) { accountBadgeView @@ -65,16 +65,17 @@ struct StatusRowHeaderView: View { .layoutPriority(1) } if !redactionReasons.contains(.placeholder) { - if (theme.displayFullUsername && theme.avatarPosition == .leading) || - theme.avatarPosition == .top { - Text("@\(theme.displayFullUsername ? viewModel.finalStatus.account.acct : viewModel.finalStatus.account.username)") - .font(.scaledFootnote) - .foregroundStyle(.secondary) - .lineLimit(1) - #if targetEnvironment(macCatalyst) - .accountPopover(viewModel.finalStatus.account) - #endif - } + if (theme.displayFullUsername && theme.avatarPosition == .leading) || + theme.avatarPosition == .top + { + Text("@\(theme.displayFullUsername ? viewModel.finalStatus.account.acct : viewModel.finalStatus.account.username)") + .font(.scaledFootnote) + .foregroundStyle(.secondary) + .lineLimit(1) + #if targetEnvironment(macCatalyst) + .accountPopover(viewModel.finalStatus.account) + #endif + } } } } @@ -92,8 +93,8 @@ struct StatusRowHeaderView: View { private var dateView: some View { Group { Text(Image(systemName: viewModel.finalStatus.visibility.iconName)) + - Text(" ⸱ ") + - Text(viewModel.finalStatus.createdAt.relativeFormatted) + Text(" ⸱ ") + + Text(viewModel.finalStatus.createdAt.relativeFormatted) } .font(.scaledFootnote) .foregroundStyle(.secondary) diff --git a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowMediaPreviewView.swift b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowMediaPreviewView.swift index 4930402f..6e2b16ef 100644 --- a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowMediaPreviewView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowMediaPreviewView.swift @@ -23,13 +23,13 @@ public struct StatusRowMediaPreviewView: View { self.sensitive = sensitive } -#if targetEnvironment(macCatalyst) - private var showsScrollIndicators: Bool { attachments.count > 1 } - private var scrollBottomPadding: CGFloat? -#else - private var showsScrollIndicators: Bool = false - private var scrollBottomPadding: CGFloat? = 0 -#endif + #if targetEnvironment(macCatalyst) + private var showsScrollIndicators: Bool { attachments.count > 1 } + private var scrollBottomPadding: CGFloat? + #else + private var showsScrollIndicators: Bool = false + private var scrollBottomPadding: CGFloat? = 0 + #endif private var imageMaxHeight: CGFloat { if isCompact { @@ -49,9 +49,9 @@ public struct StatusRowMediaPreviewView: View { if attachments.count == 1 { FeaturedImagePreView( attachment: attachments[0], - maxSize: imageMaxHeight == 300 - ? nil - : CGSize(width: imageMaxHeight, height: imageMaxHeight), + maxSize: imageMaxHeight == 300 + ? nil + : CGSize(width: imageMaxHeight, height: imageMaxHeight), sensitive: sensitive ) .accessibilityElement(children: .ignore) @@ -80,7 +80,7 @@ public struct StatusRowMediaPreviewView: View { imageMaxHeight: imageMaxHeight, displayData: data ) - .onTapGesture { + .onTapGesture { if let index = attachments.firstIndex(where: { $0.id == attachement.id }) { tabAction(for: index) } @@ -279,23 +279,23 @@ struct AltTextButton: View { .padding(EdgeInsets(top: 5, leading: 7, bottom: 5, trailing: 7)) .background(.thinMaterial) #if os(visionOS) - .clipShape(Capsule()) + .clipShape(Capsule()) #endif - .cornerRadius(4) - .padding(theme.statusDisplayStyle == .compact ? 0 : 10) - .alert( - "status.editor.media.image-description", - isPresented: $isDisplayingAlert - ) { - Button("alert.button.ok", action: {}) - } message: { - Text(text) - } - .frame( - maxWidth: .infinity, - maxHeight: .infinity, - alignment: .bottomTrailing - ) + .cornerRadius(4) + .padding(theme.statusDisplayStyle == .compact ? 0 : 10) + .alert( + "status.editor.media.image-description", + isPresented: $isDisplayingAlert + ) { + Button("alert.button.ok", action: {}) + } message: { + Text(text) + } + .frame( + maxWidth: .infinity, + maxHeight: .infinity, + alignment: .bottomTrailing + ) } } } @@ -438,9 +438,9 @@ private struct FeaturedImagePreView: View { RoundedRectangle(cornerRadius: 10) .stroke(.gray.opacity(0.35), lineWidth: 1) ) - #if os(visionOS) + #if os(visionOS) .hoverEffect() - #endif + #endif } } .overlay { @@ -468,7 +468,7 @@ private struct FeaturedImagePreView: View { self.maxSize = maxSize } - func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { + func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) -> CGSize { guard !subviews.isEmpty else { return CGSize.zero } if let maxSize { return maxSize } @@ -476,7 +476,7 @@ private struct FeaturedImagePreView: View { return calculateSize(proposal) } - func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { + func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) { guard let view = subviews.first else { return } let size = if let maxSize { maxSize } else { calculateSize(proposal) } diff --git a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowTagView.swift b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowTagView.swift index f67a21bb..28e35639 100644 --- a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowTagView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowTagView.swift @@ -5,16 +5,17 @@ import SwiftUI struct StatusRowTagView: View { @Environment(CurrentAccount.self) private var currentAccount @Environment(RouterPath.self) private var routerPath - + @Environment(\.isHomeTimeline) private var isHomeTimeline - + let viewModel: StatusRowViewModel var body: some View { if isHomeTimeline, - let tag = viewModel.finalStatus.content.links.first(where: { link in - link.type == .hashtag && currentAccount.tags.contains(where: { $0.name.lowercased() == link.title.lowercased() }) - }) { + let tag = viewModel.finalStatus.content.links.first(where: { link in + link.type == .hashtag && currentAccount.tags.contains(where: { $0.name.lowercased() == link.title.lowercased() }) + }) + { Text("#\(tag.title)") .font(.scaledFootnote) .foregroundStyle(.secondary) diff --git a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowTextView.swift b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowTextView.swift index e796637b..624bcd40 100644 --- a/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowTextView.swift +++ b/Packages/StatusKit/Sources/StatusKit/Row/Subviews/StatusRowTextView.swift @@ -7,7 +7,7 @@ import SwiftUI struct StatusRowTextView: View { @Environment(Theme.self) private var theme @Environment(\.isStatusFocused) private var isFocused - + @Environment(StatusDataController.self) private var statusDataController var viewModel: StatusRowViewModel diff --git a/Packages/Timeline/Sources/Timeline/TimelineContentFilter.swift b/Packages/Timeline/Sources/Timeline/TimelineContentFilter.swift index 5af4d4e2..5734418a 100644 --- a/Packages/Timeline/Sources/Timeline/TimelineContentFilter.swift +++ b/Packages/Timeline/Sources/Timeline/TimelineContentFilter.swift @@ -9,34 +9,34 @@ import SwiftUI @AppStorage("timeline_show_threads") var showThreads: Bool = true @AppStorage("timeline_quote_posts") var showQuotePosts: Bool = true } - + public static let shared = TimelineContentFilter() private let storage = Storage() - + public var showBoosts: Bool { didSet { storage.showBoosts = showBoosts } } - + public var showReplies: Bool { didSet { storage.showReplies = showReplies } } - + public var showThreads: Bool { didSet { storage.showThreads = showThreads } } - + public var showQuotePosts: Bool { didSet { storage.showQuotePosts = showQuotePosts } } - + private init() { showBoosts = storage.showBoosts showReplies = storage.showReplies diff --git a/Packages/Timeline/Sources/Timeline/TimelineFilter.swift b/Packages/Timeline/Sources/Timeline/TimelineFilter.swift index f60ead02..d1277709 100644 --- a/Packages/Timeline/Sources/Timeline/TimelineFilter.swift +++ b/Packages/Timeline/Sources/Timeline/TimelineFilter.swift @@ -30,7 +30,6 @@ public enum RemoteTimelineFilter: String, CaseIterable, Hashable, Equatable { } public enum TimelineFilter: Hashable, Equatable, Identifiable { - case home, local, federated, trending case hashtag(tag: String, accountId: String?) case tagGroup(title: String, tags: [String], symbolName: String?) @@ -50,9 +49,8 @@ public enum TimelineFilter: Hashable, Equatable, Identifiable { default: return title } - } - + public func hash(into hasher: inout Hasher) { hasher.combine(id) } @@ -144,7 +142,7 @@ public enum TimelineFilter: Hashable, Equatable, Identifiable { case .remoteLocal: "dot.radiowaves.right" case let .tagGroup(_, _, symbolName): - symbolName ?? "tag" + symbolName ?? "tag" case .hashtag: "number" } @@ -175,7 +173,7 @@ public enum TimelineFilter: Hashable, Equatable, Identifiable { tag: tag, onlyMedia: false, excludeReplies: false, - excludeReblogs: false, + excludeReblogs: false, pinned: nil) } else { return Timelines.hashtag(tag: tag, additional: nil, maxId: maxId, minId: minId) diff --git a/Packages/Timeline/Sources/Timeline/TimelineUnreadStatusesObserver.swift b/Packages/Timeline/Sources/Timeline/TimelineUnreadStatusesObserver.swift index 1ce4fac7..726f12fd 100644 --- a/Packages/Timeline/Sources/Timeline/TimelineUnreadStatusesObserver.swift +++ b/Packages/Timeline/Sources/Timeline/TimelineUnreadStatusesObserver.swift @@ -1,9 +1,9 @@ +import DesignSystem import Env import Foundation import Models import Observation import SwiftUI -import DesignSystem @MainActor @Observable class TimelineUnreadStatusesObserver { @@ -11,7 +11,7 @@ import DesignSystem var disableUpdate: Bool = false var scrollToIndex: ((Int) -> Void)? - + var isLoadingNewStatuses: Bool = false var pendingStatuses: [String] = [] { @@ -36,7 +36,7 @@ struct TimelineUnreadStatusesView: View { @State var observer: TimelineUnreadStatusesObserver @Environment(UserPreferences.self) private var preferences @Environment(Theme.self) private var theme - + var body: some View { if observer.pendingStatusesCount > 0 || observer.isLoadingNewStatuses { Button { @@ -59,22 +59,22 @@ struct TimelineUnreadStatusesView: View { .accessibilityLabel("accessibility.tabs.timeline.unread-posts.label-\(observer.pendingStatusesCount)") .accessibilityHint("accessibility.tabs.timeline.unread-posts.hint") #if os(visionOS) - .buttonStyle(.bordered) - .tint(Material.ultraThick) + .buttonStyle(.bordered) + .tint(Material.ultraThick) #else - .buttonStyle(.bordered) - .background(Material.ultraThick) + .buttonStyle(.bordered) + .background(Material.ultraThick) #endif - .cornerRadius(8) + .cornerRadius(8) #if !os(visionOS) - .foregroundStyle(.secondary) - .overlay( - RoundedRectangle(cornerRadius: 8) - .stroke(theme.tintColor, lineWidth: 1) - ) + .foregroundStyle(.secondary) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(theme.tintColor, lineWidth: 1) + ) #endif - .padding(8) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: preferences.pendingLocation) + .padding(8) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: preferences.pendingLocation) } } } diff --git a/Packages/Timeline/Sources/Timeline/View/TimelineContentFilterView.swift b/Packages/Timeline/Sources/Timeline/View/TimelineContentFilterView.swift index 1d85e57b..a73f2159 100644 --- a/Packages/Timeline/Sources/Timeline/View/TimelineContentFilterView.swift +++ b/Packages/Timeline/Sources/Timeline/View/TimelineContentFilterView.swift @@ -1,13 +1,13 @@ -import SwiftUI import DesignSystem +import SwiftUI public struct TimelineContentFilterView: View { @Environment(Theme.self) private var theme - + @State private var contentFilter = TimelineContentFilter.shared - + public init() {} - + public var body: some View { Form { Section { diff --git a/Packages/Timeline/Sources/Timeline/View/TimelineHeaderView.swift b/Packages/Timeline/Sources/Timeline/View/TimelineHeaderView.swift index fd1e01ca..93793868 100644 --- a/Packages/Timeline/Sources/Timeline/View/TimelineHeaderView.swift +++ b/Packages/Timeline/Sources/Timeline/View/TimelineHeaderView.swift @@ -1,11 +1,11 @@ -import SwiftUI import DesignSystem +import SwiftUI struct TimelineHeaderView: View { @Environment(Theme.self) private var theme - + var content: () -> Content - + var body: some View { VStack(alignment: .leading) { Spacer() diff --git a/Packages/Timeline/Sources/Timeline/View/TimelineQuickAccessPills.swift b/Packages/Timeline/Sources/Timeline/View/TimelineQuickAccessPills.swift index 1dc43474..a27aef46 100644 --- a/Packages/Timeline/Sources/Timeline/View/TimelineQuickAccessPills.swift +++ b/Packages/Timeline/Sources/Timeline/View/TimelineQuickAccessPills.swift @@ -1,20 +1,20 @@ -import SwiftUI +import DesignSystem import Env import Models -import DesignSystem import Network +import SwiftUI @MainActor struct TimelineQuickAccessPills: View { @Environment(Client.self) private var client @Environment(Theme.self) private var theme @Environment(CurrentAccount.self) private var currentAccount - + @Binding var pinnedFilters: [TimelineFilter] @Binding var timeline: TimelineFilter - + @State private var draggedFilter: TimelineFilter? - + var body: some View { ScrollView(.horizontal) { HStack { @@ -25,14 +25,15 @@ struct TimelineQuickAccessPills: View { } .scrollClipDisabled() .scrollIndicators(.never) - .onChange(of: currentAccount.lists, { _, lists in + .onChange(of: currentAccount.lists) { _, lists in guard client.isAuth else { return } var filters = pinnedFilters for (index, filter) in filters.enumerated() { switch filter { - case .list(let list): + case let .list(list): if let accountList = lists.first(where: { $0.id == list.id }), - accountList.title != list.title { + accountList.title != list.title + { filters[index] = .list(list: accountList) } default: @@ -40,9 +41,9 @@ struct TimelineQuickAccessPills: View { } } pinnedFilters = filters - }) + } } - + @ViewBuilder private func makePill(_ filter: TimelineFilter) -> some View { if !isFilterSupported(filter) { @@ -55,7 +56,7 @@ struct TimelineQuickAccessPills: View { .buttonStyle(.bordered) } } - + private func makeButton(_ filter: TimelineFilter) -> some View { Button { timeline = filter @@ -84,10 +85,10 @@ struct TimelineQuickAccessPills: View { items: $pinnedFilters, draggedItem: $draggedFilter)) } - + private func isFilterSupported(_ filter: TimelineFilter) -> Bool { switch filter { - case .list(let list): + case let .list(list): return currentAccount.lists.contains(where: { $0.id == list.id }) default: return true @@ -99,24 +100,24 @@ struct PillDropDelegate: DropDelegate { let destinationItem: TimelineFilter @Binding var items: [TimelineFilter] @Binding var draggedItem: TimelineFilter? - - func dropUpdated(info: DropInfo) -> DropProposal? { + + func dropUpdated(info _: DropInfo) -> DropProposal? { return DropProposal(operation: .move) } - - func performDrop(info: DropInfo) -> Bool { + + func performDrop(info _: DropInfo) -> Bool { draggedItem = nil return true } - - func dropEntered(info: DropInfo) { + + func dropEntered(info _: DropInfo) { if let draggedItem { let fromIndex = items.firstIndex(of: draggedItem) if let fromIndex { let toIndex = items.firstIndex(of: destinationItem) if let toIndex, fromIndex != toIndex { withAnimation { - self.items.move(fromOffsets: IndexSet(integer: fromIndex), toOffset: (toIndex > fromIndex ? (toIndex + 1) : toIndex)) + self.items.move(fromOffsets: IndexSet(integer: fromIndex), toOffset: toIndex > fromIndex ? (toIndex + 1) : toIndex) } } } diff --git a/Packages/Timeline/Sources/Timeline/View/TimelineTagGroupheaderView.swift b/Packages/Timeline/Sources/Timeline/View/TimelineTagGroupheaderView.swift index 7f1215d2..941daaf3 100644 --- a/Packages/Timeline/Sources/Timeline/View/TimelineTagGroupheaderView.swift +++ b/Packages/Timeline/Sources/Timeline/View/TimelineTagGroupheaderView.swift @@ -1,13 +1,13 @@ -import SwiftUI -import Models import Env +import Models +import SwiftUI struct TimelineTagGroupheaderView: View { @Environment(RouterPath.self) private var routerPath - + @Binding var group: TagGroup? @Binding var timeline: TimelineFilter - + var body: some View { if let group { TimelineHeaderView { diff --git a/Packages/Timeline/Sources/Timeline/View/TimelineTagHeaderView.swift b/Packages/Timeline/Sources/Timeline/View/TimelineTagHeaderView.swift index 7347fc70..03861daa 100644 --- a/Packages/Timeline/Sources/Timeline/View/TimelineTagHeaderView.swift +++ b/Packages/Timeline/Sources/Timeline/View/TimelineTagHeaderView.swift @@ -1,15 +1,15 @@ -import SwiftUI -import Models import DesignSystem import Env +import Models +import SwiftUI struct TimelineTagHeaderView: View { @Environment(CurrentAccount.self) private var account - + @Binding var tag: Tag? - + @State var isLoading: Bool = false - + var body: some View { if let tag { TimelineHeaderView { diff --git a/Packages/Timeline/Sources/Timeline/View/TimelineView.swift b/Packages/Timeline/Sources/Timeline/View/TimelineView.swift index 7e75f2a4..e6469249 100644 --- a/Packages/Timeline/Sources/Timeline/View/TimelineView.swift +++ b/Packages/Timeline/Sources/Timeline/View/TimelineView.swift @@ -65,17 +65,17 @@ public struct TimelineView: View { .environment(\.defaultMinListRowHeight, 1) .listStyle(.plain) #if !os(visionOS) - .scrollContentBackground(.hidden) - .background(theme.primaryBackgroundColor) + .scrollContentBackground(.hidden) + .background(theme.primaryBackgroundColor) #endif - .introspect(.list, on: .iOS(.v17)) { (collectionView: UICollectionView) in - DispatchQueue.main.async { - self.collectionView = collectionView + .introspect(.list, on: .iOS(.v17)) { (collectionView: UICollectionView) in + DispatchQueue.main.async { + self.collectionView = collectionView + } + prefetcher.viewModel = viewModel + collectionView.isPrefetchingEnabled = true + collectionView.prefetchDataSource = prefetcher } - prefetcher.viewModel = viewModel - collectionView.isPrefetchingEnabled = true - collectionView.prefetchDataSource = prefetcher - } if viewModel.timeline.supportNewestPagination { TimelineUnreadStatusesView(observer: viewModel.pendingStatusesObserver) } @@ -147,19 +147,20 @@ public struct TimelineView: View { } } } - .onChange(of: account.lists, { _, lists in + .onChange(of: account.lists) { _, lists in guard client.isAuth else { return } switch timeline { case let .list(list): if let accountList = lists.first(where: { $0.id == list.id }), - list.id == accountList.id, - accountList.title != list.title { + list.id == accountList.id, + accountList.title != list.title + { timeline = .list(list: accountList) } default: break } - }) + } .onChange(of: timeline) { oldValue, newValue in switch newValue { case let .remoteLocal(server, _): @@ -203,7 +204,7 @@ public struct TimelineView: View { case .background: wasBackgrounded = true viewModel.saveMarker() - + default: break } diff --git a/Packages/Timeline/Sources/Timeline/View/TimelineViewModel.swift b/Packages/Timeline/Sources/Timeline/View/TimelineViewModel.swift index 0ff00b94..85c9a4e8 100644 --- a/Packages/Timeline/Sources/Timeline/View/TimelineViewModel.swift +++ b/Packages/Timeline/Sources/Timeline/View/TimelineViewModel.swift @@ -11,7 +11,7 @@ import SwiftUI var statusesState: StatusesState = .loading var timeline: TimelineFilter = .federated { willSet { - if timeline == .home && newValue != .resume { + if timeline == .home, newValue != .resume { saveMarker() } } @@ -19,17 +19,17 @@ import SwiftUI timelineTask?.cancel() timelineTask = Task { await handleLatestOrResume(oldValue) - + if oldValue != timeline { await reset() pendingStatusesObserver.pendingStatuses = [] tag = nil } - + guard !Task.isCancelled else { return } - + await fetchNewestStatuses(pullToRefresh: false) switch timeline { case let .hashtag(tag, _): @@ -51,10 +51,10 @@ import SwiftUI private var isCacheEnabled: Bool { canFilterTimeline && timeline.supportNewestPagination } - + @ObservationIgnored private var visibileStatuses: [Status] = [] - + private var canStreamEvents: Bool = true { didSet { if canStreamEvents { @@ -62,7 +62,7 @@ import SwiftUI } } } - + @ObservationIgnored var canFilterTimeline: Bool = true @@ -92,7 +92,7 @@ import SwiftUI let pendingStatusesObserver: TimelineUnreadStatusesObserver = .init() var scrollToIndexAnimated: Bool = false var marker: Marker.Content? - + init() { pendingStatusesObserver.scrollToIndex = { [weak self] index in self?.scrollToIndexAnimated = true @@ -113,7 +113,7 @@ import SwiftUI func reset() async { await datasource.reset() } - + private func handleLatestOrResume(_ oldValue: TimelineFilter) async { if timeline == .latest || timeline == .resume { await clearCache(filter: oldValue) @@ -204,7 +204,7 @@ extension TimelineViewModel: StatusesFetcher { await fetchNewestStatuses(pullToRefresh: false) } } - + func refreshTimelineContentFilter() async { timelineTask?.cancel() let statuses = await datasource.getFiltered() @@ -212,7 +212,7 @@ extension TimelineViewModel: StatusesFetcher { statusesState = .display(statuses: statuses, nextPageState: .hasNextPage) } } - + func fetchStatuses(from: Marker.Content) async throws { guard let client else { return } statusesState = .loading @@ -231,7 +231,7 @@ extension TimelineViewModel: StatusesFetcher { withAnimation { statusesState = .display(statuses: statuses, nextPageState: .hasNextPage) } - + await fetchNewestStatuses(pullToRefresh: false) } @@ -355,7 +355,7 @@ extension TimelineViewModel: StatusesFetcher { // High chance the user is scrolled to the top. // We need to update the statuses state, and then scroll to the previous top most status. - if let topStatus, visibileStatuses.contains(where: { $0.id == topStatus.id}), scrollToTopVisible { + if let topStatus, visibileStatuses.contains(where: { $0.id == topStatus.id }), scrollToTopVisible { pendingStatusesObserver.disableUpdate = true let statuses = await datasource.getFiltered() statusesState = .display(statuses: statuses, @@ -416,7 +416,7 @@ extension TimelineViewModel: StatusesFetcher { enum NextPageError: Error { case internalError } - + func fetchNextPage() async throws { let statuses = await datasource.get() guard let client, let lastId = statuses.last?.id else { throw NextPageError.internalError } @@ -425,7 +425,6 @@ extension TimelineViewModel: StatusesFetcher { minId: nil, offset: statuses.count)) - await datasource.append(contentOf: newStatuses) StatusDataControllerProvider.shared.updateDataControllers(for: newStatuses, client: client) @@ -436,7 +435,7 @@ extension TimelineViewModel: StatusesFetcher { func statusDidAppear(status: Status) { pendingStatusesObserver.removeStatus(status: status) visibileStatuses.insert(status, at: 0) - + if let client, timeline.supportNewestPagination { Task { await cache.setLatestSeenStatuses(visibileStatuses, for: client, filter: timeline.id) @@ -450,6 +449,7 @@ extension TimelineViewModel: StatusesFetcher { } // MARK: - MARKER + extension TimelineViewModel { func fetchMarker() async -> Marker.Content? { guard let client else { @@ -462,14 +462,14 @@ extension TimelineViewModel { return nil } } - + func saveMarker() { guard timeline == .home, let client else { return } Task { guard let id = await cache.getLatestSeenStatus(for: client, filter: timeline.id)?.first else { return } do { let _: Marker = try await client.post(endpoint: Markers.markHome(lastReadId: id)) - } catch { } + } catch {} } } } diff --git a/Packages/Timeline/Sources/Timeline/actors/TimelineCache.swift b/Packages/Timeline/Sources/Timeline/actors/TimelineCache.swift index de2deab5..f2dd91d2 100644 --- a/Packages/Timeline/Sources/Timeline/actors/TimelineCache.swift +++ b/Packages/Timeline/Sources/Timeline/actors/TimelineCache.swift @@ -16,7 +16,7 @@ public actor TimelineCache { private let encoder = JSONEncoder() public init() {} - + public func cachedPostsCount(for client: String) async -> Int { do { let directory = FileManager.Directory.defaultStorageDirectory(appendingPath: client).url @@ -32,7 +32,7 @@ public actor TimelineCache { return 0 } } - + public func clearCache(for client: String) async { let directory = FileManager.Directory.defaultStorageDirectory(appendingPath: client) try? FileManager.default.removeItem(at: directory.url) @@ -73,9 +73,9 @@ public actor TimelineCache { func setLatestSeenStatuses(_ statuses: [Status], for client: Client, filter: String) { let statuses = statuses.sorted(by: { $0.createdAt.asDate > $1.createdAt.asDate }) if filter == "Home" { - UserDefaults.standard.set(statuses.map{ $0.id }, forKey: "timeline-last-seen-\(client.id)") + UserDefaults.standard.set(statuses.map { $0.id }, forKey: "timeline-last-seen-\(client.id)") } else { - UserDefaults.standard.set(statuses.map{ $0.id }, forKey: "timeline-last-seen-\(client.id)-\(filter)") + UserDefaults.standard.set(statuses.map { $0.id }, forKey: "timeline-last-seen-\(client.id)-\(filter)") } } diff --git a/Packages/Timeline/Sources/Timeline/actors/TimelineDatasource.swift b/Packages/Timeline/Sources/Timeline/actors/TimelineDatasource.swift index 6c663d29..ac094522 100644 --- a/Packages/Timeline/Sources/Timeline/actors/TimelineDatasource.swift +++ b/Packages/Timeline/Sources/Timeline/actors/TimelineDatasource.swift @@ -1,6 +1,6 @@ +import Env import Foundation import Models -import Env actor TimelineDatasource { private var statuses: [Status] = [] @@ -12,7 +12,7 @@ actor TimelineDatasource { func get() -> [Status] { statuses } - + func getFiltered() async -> [Status] { let contentFilter = TimelineContentFilter.shared let showReplies = await contentFilter.showReplies @@ -21,16 +21,17 @@ actor TimelineDatasource { let showQuotePosts = await contentFilter.showQuotePosts return statuses.filter { status in if status.isHidden || - !showReplies && status.inReplyToId != nil && status.inReplyToAccountId != status.account.id || - !showBoosts && status.reblog != nil || - !showThreads && status.inReplyToAccountId == status.account.id || - !showQuotePosts && !status.content.statusesURLs.isEmpty { + !showReplies && status.inReplyToId != nil && status.inReplyToAccountId != status.account.id || + !showBoosts && status.reblog != nil || + !showThreads && status.inReplyToAccountId == status.account.id || + !showQuotePosts && !status.content.statusesURLs.isEmpty + { return false } return true } } - + func count() -> Int { statuses.count } diff --git a/Packages/Timeline/Tests/TimelineTests/TimelineFilterTests.swift b/Packages/Timeline/Tests/TimelineTests/TimelineFilterTests.swift index 74ddf5ab..31d544b9 100644 --- a/Packages/Timeline/Tests/TimelineTests/TimelineFilterTests.swift +++ b/Packages/Timeline/Tests/TimelineTests/TimelineFilterTests.swift @@ -1,8 +1,7 @@ +import Models +import Network @testable import Timeline import XCTest -import Network -import Models - final class TimelineFilterTests: XCTestCase { func testCodableHome() throws { @@ -15,7 +14,7 @@ final class TimelineFilterTests: XCTestCase { XCTAssertTrue(try testCodableOn(filter: .hashtag(tag: "test", accountId: nil))) XCTAssertTrue(try testCodableOn(filter: .list(list: .init(id: "test", title: "test")))) } - + private func testCodableOn(filter: TimelineFilter) throws -> Bool { let encoder = JSONEncoder() let decoder = JSONDecoder() diff --git a/Packages/Timeline/Tests/TimelineTests/TimelineViewModelTests.swift b/Packages/Timeline/Tests/TimelineTests/TimelineViewModelTests.swift index 5d58c754..310bd1f4 100644 --- a/Packages/Timeline/Tests/TimelineTests/TimelineViewModelTests.swift +++ b/Packages/Timeline/Tests/TimelineTests/TimelineViewModelTests.swift @@ -1,12 +1,12 @@ +import Models +import Network @testable import Timeline import XCTest -import Network -import Models @MainActor final class TimelineViewModelTests: XCTestCase { var subject = TimelineViewModel() - + override func setUp() async throws { subject = TimelineViewModel() let client = Client(server: "localhost") @@ -15,7 +15,7 @@ final class TimelineViewModelTests: XCTestCase { subject.isTimelineVisible = true subject.timelineTask?.cancel() } - + func testStreamEventInsertNewStatus() async throws { let isEmpty = await subject.datasource.isEmpty XCTAssertTrue(isEmpty) @@ -26,7 +26,7 @@ final class TimelineViewModelTests: XCTestCase { count = await subject.datasource.count() XCTAssertTrue(count == 2) } - + func testStreamEventInsertDuplicateStatus() async throws { let isEmpty = await subject.datasource.isEmpty XCTAssertTrue(isEmpty) @@ -38,7 +38,7 @@ final class TimelineViewModelTests: XCTestCase { count = await subject.datasource.count() XCTAssertTrue(count == 1) } - + func testStreamEventRemove() async throws { let isEmpty = await subject.datasource.isEmpty XCTAssertTrue(isEmpty) @@ -50,7 +50,7 @@ final class TimelineViewModelTests: XCTestCase { count = await subject.datasource.count() XCTAssertTrue(count == 0) } - + func testStreamEventUpdateStatus() async throws { var status = Status.placeholder() let isEmpty = await subject.datasource.isEmpty @@ -65,9 +65,9 @@ final class TimelineViewModelTests: XCTestCase { editedAt: status.editedAt, reblog: status.reblog, mediaAttachments: status.mediaAttachments, - mentions: status.mentions, + mentions: status.mentions, repliesCount: status.repliesCount, - reblogsCount: status.reblogsCount, + reblogsCount: status.reblogsCount, favouritesCount: status.favouritesCount, card: status.card, favourited: status.favourited,