mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-06-10 09:19:22 +00:00
Initial iOS 17 + Observable migration
This commit is contained in:
parent
98035e8530
commit
54839953cd
|
@ -975,7 +975,7 @@
|
|||
INFOPLIST_FILE = IceCubesNotifications/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = IceCubesNotifications;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -1005,7 +1005,7 @@
|
|||
INFOPLIST_FILE = IceCubesNotifications/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = IceCubesNotifications;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -1036,7 +1036,7 @@
|
|||
INFOPLIST_FILE = IceCubesShareExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Ice Cubes";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -1066,7 +1066,7 @@
|
|||
INFOPLIST_FILE = IceCubesShareExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Ice Cubes";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -1240,7 +1240,7 @@
|
|||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
|
@ -1293,7 +1293,7 @@
|
|||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
|
@ -1325,7 +1325,7 @@
|
|||
INFOPLIST_FILE = IceCubesActionExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Open in Ice Cube";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -1356,7 +1356,7 @@
|
|||
INFOPLIST_FILE = IceCubesActionExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Open in Ice Cube";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
|
|
@ -116,9 +116,9 @@ extension View {
|
|||
.environmentObject(UserPreferences.shared)
|
||||
.environmentObject(CurrentInstance.shared)
|
||||
.environmentObject(Theme.shared)
|
||||
.environmentObject(AppAccountsManager.shared)
|
||||
.environment(AppAccountsManager.shared)
|
||||
.environmentObject(PushNotificationsService.shared)
|
||||
.environmentObject(AppAccountsManager.shared.currentClient)
|
||||
.environment(AppAccountsManager.shared.currentClient)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ struct IceCubesApp: App {
|
|||
|
||||
@Environment(\.scenePhase) private var scenePhase
|
||||
|
||||
@StateObject private var appAccountsManager = AppAccountsManager.shared
|
||||
@State private var appAccountsManager = AppAccountsManager.shared
|
||||
@StateObject private var currentInstance = CurrentInstance.shared
|
||||
@StateObject private var currentAccount = CurrentAccount.shared
|
||||
@StateObject private var userPreferences = UserPreferences.shared
|
||||
|
@ -43,8 +43,8 @@ struct IceCubesApp: App {
|
|||
setupRevenueCat()
|
||||
refreshPushSubs()
|
||||
}
|
||||
.environmentObject(appAccountsManager)
|
||||
.environmentObject(appAccountsManager.currentClient)
|
||||
.environment(appAccountsManager)
|
||||
.environment(appAccountsManager.currentClient)
|
||||
.environmentObject(quickLook)
|
||||
.environmentObject(currentAccount)
|
||||
.environmentObject(currentInstance)
|
||||
|
@ -58,17 +58,17 @@ struct IceCubesApp: App {
|
|||
.edgesIgnoringSafeArea(.bottom)
|
||||
.background(TransparentBackground())
|
||||
})
|
||||
.onChange(of: pushNotificationsService.handledNotification) { notification in
|
||||
if notification != nil {
|
||||
.onChange(of: pushNotificationsService.handledNotification) { oldValue, newValue in
|
||||
if newValue != nil {
|
||||
pushNotificationsService.handledNotification = nil
|
||||
if appAccountsManager.currentAccount.oauthToken?.accessToken != notification?.account.token.accessToken,
|
||||
if appAccountsManager.currentAccount.oauthToken?.accessToken != newValue?.account.token.accessToken,
|
||||
let account = appAccountsManager.availableAccounts.first(where:
|
||||
{ $0.oauthToken?.accessToken == notification?.account.token.accessToken })
|
||||
{ $0.oauthToken?.accessToken == newValue?.account.token.accessToken })
|
||||
{
|
||||
appAccountsManager.currentAccount = account
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
||||
selectedTab = .notifications
|
||||
pushNotificationsService.handledNotification = notification
|
||||
pushNotificationsService.handledNotification = newValue
|
||||
}
|
||||
} else {
|
||||
selectedTab = .notifications
|
||||
|
@ -79,12 +79,12 @@ struct IceCubesApp: App {
|
|||
.commands {
|
||||
appMenu
|
||||
}
|
||||
.onChange(of: scenePhase) { scenePhase in
|
||||
handleScenePhase(scenePhase: scenePhase)
|
||||
.onChange(of: scenePhase) { oldValue, newValue in
|
||||
handleScenePhase(scenePhase: newValue)
|
||||
}
|
||||
.onChange(of: appAccountsManager.currentClient) { newClient in
|
||||
setNewClientsInEnv(client: newClient)
|
||||
if newClient.isAuth {
|
||||
.onChange(of: appAccountsManager.currentClient) { oldValue, newValue in
|
||||
setNewClientsInEnv(client: newValue)
|
||||
if newValue.isAuth {
|
||||
watcher.watch(streams: [.user, .direct])
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ struct IceCubesApp: App {
|
|||
}
|
||||
}
|
||||
}
|
||||
}.onChange(of: $appAccountsManager.currentAccount.id) { _ in
|
||||
}.onChange(of: $appAccountsManager.currentAccount.id) {
|
||||
sideBarLoadedTabs.removeAll()
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ struct IceCubesApp: App {
|
|||
watcher.stopWatching()
|
||||
case .active:
|
||||
watcher.watch(streams: [.user, .direct])
|
||||
UIApplication.shared.applicationIconBadgeNumber = 0
|
||||
UNUserNotificationCenter.current().setBadgeCount(0)
|
||||
Task {
|
||||
await userPreferences.refreshServerPreferences()
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ public struct ReportView: View {
|
|||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
|
||||
let status: Status
|
||||
@State private var commentText: String = ""
|
||||
|
|
|
@ -2,6 +2,7 @@ import DesignSystem
|
|||
import Env
|
||||
import SafariServices
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
extension View {
|
||||
@MainActor func withSafariRouter() -> some View {
|
||||
|
@ -15,7 +16,7 @@ private struct SafariRouter: ViewModifier {
|
|||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
|
||||
@StateObject private var safariManager = InAppSafariManager()
|
||||
@State private var safariManager = InAppSafariManager()
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
|
@ -58,7 +59,7 @@ private struct SafariRouter: ViewModifier {
|
|||
}
|
||||
|
||||
@MainActor
|
||||
private class InAppSafariManager: NSObject, ObservableObject, SFSafariViewControllerDelegate {
|
||||
@Observable private class InAppSafariManager: NSObject, SFSafariViewControllerDelegate {
|
||||
var windowScene: UIWindowScene?
|
||||
let viewController: UIViewController = .init()
|
||||
var window: UIWindow?
|
||||
|
|
|
@ -6,7 +6,7 @@ import Models
|
|||
import SwiftUI
|
||||
|
||||
struct SideBarView<Content: View>: View {
|
||||
@EnvironmentObject private var appAccounts: AppAccountsManager
|
||||
@Environment(AppAccountsManager.self) private var appAccounts
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var watcher: StreamWatcher
|
||||
|
|
|
@ -11,7 +11,7 @@ struct ExploreTab: View {
|
|||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@StateObject private var routerPath = RouterPath()
|
||||
@Binding var popToRootTab: Tab
|
||||
|
||||
|
@ -36,12 +36,12 @@ struct ExploreTab: View {
|
|||
}
|
||||
.withSafariRouter()
|
||||
.environmentObject(routerPath)
|
||||
.onChange(of: $popToRootTab.wrappedValue) { popToRootTab in
|
||||
if popToRootTab == .explore {
|
||||
.onChange(of: $popToRootTab.wrappedValue) { oldValue, newValue in
|
||||
if newValue == .explore {
|
||||
routerPath.path = []
|
||||
}
|
||||
}
|
||||
.onChange(of: client.id) { _ in
|
||||
.onChange(of: client.id) {
|
||||
routerPath.path = []
|
||||
}
|
||||
.onAppear {
|
||||
|
|
|
@ -11,9 +11,9 @@ import SwiftUI
|
|||
struct MessagesTab: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var watcher: StreamWatcher
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var appAccount: AppAccountsManager
|
||||
@Environment(AppAccountsManager.self) private var appAccount
|
||||
@StateObject private var routerPath = RouterPath()
|
||||
@Binding var popToRootTab: Tab
|
||||
|
||||
|
@ -32,12 +32,12 @@ struct MessagesTab: View {
|
|||
.toolbarBackground(theme.primaryBackgroundColor.opacity(0.50), for: .navigationBar)
|
||||
.id(client.id)
|
||||
}
|
||||
.onChange(of: $popToRootTab.wrappedValue) { popToRootTab in
|
||||
if popToRootTab == .messages {
|
||||
.onChange(of: $popToRootTab.wrappedValue) { oldValue, newValue in
|
||||
if newValue == .messages {
|
||||
routerPath.path = []
|
||||
}
|
||||
}
|
||||
.onChange(of: client.id) { _ in
|
||||
.onChange(of: client.id) {
|
||||
routerPath.path = []
|
||||
}
|
||||
.onAppear {
|
||||
|
|
|
@ -12,9 +12,9 @@ struct NotificationsTab: View {
|
|||
@Environment(\.scenePhase) private var scenePhase
|
||||
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var watcher: StreamWatcher
|
||||
@EnvironmentObject private var appAccount: AppAccountsManager
|
||||
@Environment(AppAccountsManager.self) private var appAccount
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var userPreferences: UserPreferences
|
||||
@EnvironmentObject private var pushNotificationsService: PushNotificationsService
|
||||
|
@ -55,34 +55,34 @@ struct NotificationsTab: View {
|
|||
}
|
||||
.withSafariRouter()
|
||||
.environmentObject(routerPath)
|
||||
.onChange(of: $popToRootTab.wrappedValue) { popToRootTab in
|
||||
if popToRootTab == .notifications {
|
||||
.onChange(of: $popToRootTab.wrappedValue) { oldValue, newValue in
|
||||
if newValue == .notifications {
|
||||
routerPath.path = []
|
||||
}
|
||||
}
|
||||
.onChange(of: pushNotificationsService.handledNotification) { notification in
|
||||
if let notification, let type = notification.notification.supportedType {
|
||||
.onChange(of: pushNotificationsService.handledNotification) { oldValue, newValue in
|
||||
if let newValue, let type = newValue.notification.supportedType {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||
switch type {
|
||||
case .follow, .follow_request:
|
||||
routerPath.navigate(to: .accountDetailWithAccount(account: notification.notification.account))
|
||||
routerPath.navigate(to: .accountDetailWithAccount(account: newValue.notification.account))
|
||||
default:
|
||||
if let status = notification.notification.status {
|
||||
if let status = newValue.notification.status {
|
||||
routerPath.navigate(to: .statusDetailWithStatus(status: status))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: scenePhase, perform: { scenePhase in
|
||||
switch scenePhase {
|
||||
.onChange(of: scenePhase) { oldValue, newValue in
|
||||
switch newValue {
|
||||
case .active:
|
||||
clearNotifications()
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
.onChange(of: client.id) { _ in
|
||||
}
|
||||
.onChange(of: client.id) {
|
||||
routerPath.path = []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ import Shimmer
|
|||
import SwiftUI
|
||||
|
||||
struct ProfileTab: View {
|
||||
@EnvironmentObject private var appAccount: AppAccountsManager
|
||||
@Environment(AppAccountsManager.self) private var appAccount
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@StateObject private var routerPath = RouterPath()
|
||||
@Binding var popToRootTab: Tab
|
||||
|
@ -29,12 +29,12 @@ struct ProfileTab: View {
|
|||
.redacted(reason: .placeholder)
|
||||
}
|
||||
}
|
||||
.onChange(of: $popToRootTab.wrappedValue) { popToRootTab in
|
||||
if popToRootTab == .profile {
|
||||
.onChange(of: $popToRootTab.wrappedValue) { oldValue, newValue in
|
||||
if newValue == .profile {
|
||||
routerPath.path = []
|
||||
}
|
||||
}
|
||||
.onChange(of: client.id) { _ in
|
||||
.onChange(of: client.id) {
|
||||
routerPath.path = []
|
||||
}
|
||||
.onAppear {
|
||||
|
|
|
@ -15,8 +15,8 @@ struct AccountSettingsView: View {
|
|||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var currentInstance: CurrentInstance
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var appAccountsManager: AppAccountsManager
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(AppAccountsManager.self) private var appAccountsManager
|
||||
@Environment(Client.self) private var client
|
||||
|
||||
@State private var isEditingAccount: Bool = false
|
||||
@State private var isEditingFilters: Bool = false
|
||||
|
|
|
@ -13,7 +13,7 @@ struct AddAccountView: View {
|
|||
@Environment(\.dismiss) private var dismiss
|
||||
@Environment(\.scenePhase) private var scenePhase
|
||||
|
||||
@EnvironmentObject private var appAccountsManager: AppAccountsManager
|
||||
@Environment(AppAccountsManager.self) private var appAccountsManager
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var currentInstance: CurrentInstance
|
||||
@EnvironmentObject private var pushNotifications: PushNotificationsService
|
||||
|
@ -89,7 +89,7 @@ struct AddAccountView: View {
|
|||
}
|
||||
isSigninIn = false
|
||||
}
|
||||
.onChange(of: instanceName) { newValue in
|
||||
.onChange(of: instanceName) { oldValue, newValue in
|
||||
instanceNamePublisher.send(newValue)
|
||||
}
|
||||
.onReceive(instanceNamePublisher.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)) { _ in
|
||||
|
@ -119,24 +119,24 @@ struct AddAccountView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: scenePhase, perform: { scenePhase in
|
||||
switch scenePhase {
|
||||
.onChange(of: scenePhase) { oldValue, newValue in
|
||||
switch newValue {
|
||||
case .active:
|
||||
isSigninIn = false
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
.onOpenURL(perform: { url in
|
||||
Task {
|
||||
await continueSignIn(url: url)
|
||||
}
|
||||
})
|
||||
.onChange(of: oauthURL, perform: { newValue in
|
||||
.onChange(of: oauthURL) { oldValue, newValue in
|
||||
if newValue == nil {
|
||||
isSigninIn = false
|
||||
}
|
||||
})
|
||||
}
|
||||
.sheet(item: $oauthURL, content: { url in
|
||||
SafariView(url: url)
|
||||
})
|
||||
|
|
|
@ -44,7 +44,7 @@ struct ContentSettingsView: View {
|
|||
}
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
.onChange(of: userPreferences.useInstanceContentSettings) { newVal in
|
||||
.onChange(of: userPreferences.useInstanceContentSettings) { oldValue, newVal in
|
||||
if newVal {
|
||||
userPreferences.appAutoExpandSpoilers = userPreferences.autoExpandSpoilers
|
||||
userPreferences.appAutoExpandMedia = userPreferences.autoExpandMedia
|
||||
|
@ -93,7 +93,7 @@ struct ContentSettingsView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: userPreferences.postVisibility) { _ in
|
||||
.onChange(of: userPreferences.postVisibility) {
|
||||
userPreferences.conformReplyVisibilityConstraints()
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import Models
|
|||
import Network
|
||||
import Status
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
class DisplaySettingsLocalValues: ObservableObject {
|
||||
@Published var tintColor = Theme.shared.tintColor
|
||||
|
@ -64,7 +65,7 @@ struct DisplaySettingsView: View {
|
|||
var body: some View {
|
||||
ZStack(alignment: .top) {
|
||||
Form {
|
||||
StatusRowView(viewModel: { previewStatusViewModel })
|
||||
StatusRowView(viewModel: previewStatusViewModel)
|
||||
.allowsHitTesting(false)
|
||||
.opacity(0)
|
||||
.hidden()
|
||||
|
@ -83,7 +84,7 @@ struct DisplaySettingsView: View {
|
|||
|
||||
private var examplePost: some View {
|
||||
VStack(spacing: 0) {
|
||||
StatusRowView(viewModel: { previewStatusViewModel })
|
||||
StatusRowView(viewModel: previewStatusViewModel)
|
||||
.allowsHitTesting(false)
|
||||
.padding(.layoutPadding)
|
||||
.background(theme.primaryBackgroundColor)
|
||||
|
@ -111,7 +112,7 @@ struct DisplaySettingsView: View {
|
|||
}
|
||||
.disabled(theme.followSystemColorScheme)
|
||||
.opacity(theme.followSystemColorScheme ? 0.5 : 1.0)
|
||||
.onChange(of: theme.selectedSet) { _ in
|
||||
.onChange(of: theme.selectedSet) {
|
||||
localValues.tintColor = theme.tintColor
|
||||
localValues.primaryBackgroundColor = theme.primaryBackgroundColor
|
||||
localValues.secondaryBackgroundColor = theme.secondaryBackgroundColor
|
||||
|
|
|
@ -9,7 +9,7 @@ import UserNotifications
|
|||
|
||||
struct PushNotificationsView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var appAccountsManager: AppAccountsManager
|
||||
@Environment(AppAccountsManager.self) private var appAccountsManager
|
||||
@EnvironmentObject private var pushNotifications: PushNotificationsService
|
||||
|
||||
@StateObject public var subscription: PushNotificationSubscriptionSettings
|
||||
|
|
|
@ -14,9 +14,9 @@ struct SettingsTabs: View {
|
|||
|
||||
@EnvironmentObject private var pushNotifications: PushNotificationsService
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var currentInstance: CurrentInstance
|
||||
@EnvironmentObject private var appAccountsManager: AppAccountsManager
|
||||
@Environment(AppAccountsManager.self) private var appAccountsManager
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
@StateObject private var routerPath = RouterPath()
|
||||
|
@ -68,8 +68,8 @@ struct SettingsTabs: View {
|
|||
}
|
||||
.withSafariRouter()
|
||||
.environmentObject(routerPath)
|
||||
.onChange(of: $popToRootTab.wrappedValue) { popToRootTab in
|
||||
if popToRootTab == .notifications {
|
||||
.onChange(of: $popToRootTab.wrappedValue) { oldValue, newValue in
|
||||
if newValue == .notifications {
|
||||
routerPath.path = []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ struct SwipeActionsSettingsView: View {
|
|||
|
||||
createStatusActionPicker(selection: $userPreferences.swipeActionsStatusLeadingLeft,
|
||||
label: "settings.swipeactions.primary")
|
||||
.onChange(of: userPreferences.swipeActionsStatusLeadingLeft) { action in
|
||||
.onChange(of: userPreferences.swipeActionsStatusLeadingLeft) { _, action in
|
||||
if action == .none {
|
||||
userPreferences.swipeActionsStatusLeadingRight = .none
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ struct SwipeActionsSettingsView: View {
|
|||
|
||||
createStatusActionPicker(selection: $userPreferences.swipeActionsStatusTrailingRight,
|
||||
label: "settings.swipeactions.primary")
|
||||
.onChange(of: userPreferences.swipeActionsStatusTrailingRight) { action in
|
||||
.onChange(of: userPreferences.swipeActionsStatusTrailingRight) { _, action in
|
||||
if action == .none {
|
||||
userPreferences.swipeActionsStatusTrailingLeft = .none
|
||||
}
|
||||
|
|
|
@ -52,7 +52,9 @@ struct TranslationSettingsView: View {
|
|||
.navigationTitle("settings.translation.navigation-title")
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(theme.secondaryBackgroundColor)
|
||||
.onChange(of: apiKey, perform: writeNewValue)
|
||||
.onChange(of: apiKey) {
|
||||
writeNewValue()
|
||||
}
|
||||
.onAppear(perform: updatePrefs)
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ struct AddRemoteTimelineView: View {
|
|||
Button("action.cancel", action: { dismiss() })
|
||||
}
|
||||
}
|
||||
.onChange(of: instanceName) { newValue in
|
||||
.onChange(of: instanceName) { oldValue, newValue in
|
||||
instanceNamePublisher.send(newValue)
|
||||
}
|
||||
.onReceive(instanceNamePublisher.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)) { newValue in
|
||||
|
|
|
@ -93,7 +93,7 @@ struct EditTagGroupView: View {
|
|||
.onSubmit {
|
||||
focusedField = Focus.new
|
||||
}
|
||||
.onChange(of: sfSymbolName) { _ in
|
||||
.onChange(of: sfSymbolName) {
|
||||
popupTagsPresented = true
|
||||
}
|
||||
|
||||
|
|
|
@ -8,11 +8,11 @@ import SwiftUI
|
|||
import Timeline
|
||||
|
||||
struct TimelineTab: View {
|
||||
@EnvironmentObject private var appAccount: AppAccountsManager
|
||||
@Environment(AppAccountsManager.self) private var appAccount
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@StateObject private var routerPath = RouterPath()
|
||||
@Binding var popToRootTab: Tab
|
||||
|
||||
|
@ -58,22 +58,22 @@ struct TimelineTab: View {
|
|||
routerPath.presentedSheet = .addAccount
|
||||
}
|
||||
}
|
||||
.onChange(of: client.isAuth, perform: { _ in
|
||||
.onChange(of: client.isAuth) {
|
||||
if client.isAuth {
|
||||
timeline = lastTimelineFilter
|
||||
} else {
|
||||
timeline = .federated
|
||||
}
|
||||
})
|
||||
.onChange(of: currentAccount.account?.id, perform: { _ in
|
||||
}
|
||||
.onChange(of: currentAccount.account?.id) {
|
||||
if client.isAuth, canFilterTimeline {
|
||||
timeline = lastTimelineFilter
|
||||
} else {
|
||||
timeline = .federated
|
||||
}
|
||||
})
|
||||
.onChange(of: $popToRootTab.wrappedValue) { popToRootTab in
|
||||
if popToRootTab == .timeline {
|
||||
}
|
||||
.onChange(of: $popToRootTab.wrappedValue) { oldValue, newValue in
|
||||
if newValue == .timeline {
|
||||
if routerPath.path.isEmpty {
|
||||
scrollToTopSignal += 1
|
||||
} else {
|
||||
|
@ -81,12 +81,12 @@ struct TimelineTab: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: client.id) { _ in
|
||||
.onChange(of: client.id) {
|
||||
routerPath.path = []
|
||||
}
|
||||
.onChange(of: timeline) { timeline in
|
||||
if timeline == .home || timeline == .federated || timeline == .local {
|
||||
lastTimelineFilter = timeline
|
||||
.onChange(of: timeline) { oldValue, newValue in
|
||||
if newValue == .home || newValue == .federated || newValue == .local {
|
||||
lastTimelineFilter = newValue
|
||||
}
|
||||
}
|
||||
.withSafariRouter()
|
||||
|
|
|
@ -28,8 +28,8 @@ class ShareViewController: UIViewController {
|
|||
if let attachments = item.attachments {
|
||||
let view = StatusEditorView(mode: .shareExtension(items: attachments))
|
||||
.environmentObject(UserPreferences.shared)
|
||||
.environmentObject(appAccountsManager)
|
||||
.environmentObject(client)
|
||||
.environment(appAccountsManager)
|
||||
.environment(client)
|
||||
.environmentObject(account)
|
||||
.environmentObject(theme)
|
||||
.environmentObject(instance)
|
||||
|
|
|
@ -7,7 +7,7 @@ let package = Package(
|
|||
name: "Account",
|
||||
defaultLocalization: "en",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.iOS(.v17),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
|
|
@ -3,12 +3,12 @@ import Network
|
|||
import SwiftUI
|
||||
|
||||
public struct AccountDetailContextMenu: View {
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
@EnvironmentObject private var currentInstance: CurrentInstance
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
|
||||
@ObservedObject var viewModel: AccountDetailViewModel
|
||||
var viewModel: AccountDetailViewModel
|
||||
|
||||
public var body: some View {
|
||||
if let account = viewModel.account {
|
||||
|
|
|
@ -18,7 +18,7 @@ struct AccountDetailHeaderView: View {
|
|||
@Environment(\.redactionReasons) private var reasons
|
||||
@Environment(\.isSupporter) private var isSupporter: Bool
|
||||
|
||||
@ObservedObject var viewModel: AccountDetailViewModel
|
||||
var viewModel: AccountDetailViewModel
|
||||
let account: Account
|
||||
let scrollViewProxy: ScrollViewProxy?
|
||||
|
||||
|
|
|
@ -16,10 +16,10 @@ public struct AccountDetailView: View {
|
|||
@EnvironmentObject private var currentInstance: CurrentInstance
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
|
||||
@StateObject private var viewModel: AccountDetailViewModel
|
||||
@State private var viewModel: AccountDetailViewModel
|
||||
@State private var isCurrentUser: Bool = false
|
||||
@State private var isCreateListAlertPresented: Bool = false
|
||||
@State private var createListTitle: String = ""
|
||||
|
@ -30,12 +30,12 @@ public struct AccountDetailView: View {
|
|||
|
||||
/// When coming from a URL like a mention tap in a status.
|
||||
public init(accountId: String) {
|
||||
_viewModel = StateObject(wrappedValue: .init(accountId: accountId))
|
||||
_viewModel = .init(initialValue: .init(accountId: accountId))
|
||||
}
|
||||
|
||||
/// When the account is already fetched by the parent caller.
|
||||
public init(account: Account) {
|
||||
_viewModel = StateObject(wrappedValue: .init(account: account))
|
||||
_viewModel = .init(initialValue: .init(account: account))
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
|
@ -114,21 +114,21 @@ public struct AccountDetailView: View {
|
|||
SoundEffectManager.shared.playSound(of: .refresh)
|
||||
}
|
||||
}
|
||||
.onChange(of: watcher.latestEvent?.id) { _ in
|
||||
.onChange(of: watcher.latestEvent?.id) {
|
||||
if let latestEvent = watcher.latestEvent,
|
||||
viewModel.accountId == currentAccount.account?.id
|
||||
{
|
||||
viewModel.handleEvent(event: latestEvent, currentAccount: currentAccount)
|
||||
}
|
||||
}
|
||||
.onChange(of: isEditingAccount, perform: { isEditing in
|
||||
if !isEditing {
|
||||
.onChange(of: isEditingAccount) { oldValue, newValue in
|
||||
if !newValue {
|
||||
Task {
|
||||
await viewModel.fetchAccount()
|
||||
await preferences.refreshServerPreferences()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
.sheet(isPresented: $isEditingAccount, content: {
|
||||
EditAccountView()
|
||||
})
|
||||
|
@ -292,7 +292,7 @@ public struct AccountDetailView: View {
|
|||
.listRowSeparator(.hidden)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
ForEach(viewModel.pinned) { status in
|
||||
StatusRowView(viewModel: { .init(status: status, client: client, routerPath: routerPath) })
|
||||
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath) )
|
||||
}
|
||||
Rectangle()
|
||||
.fill(theme.secondaryBackgroundColor)
|
||||
|
|
|
@ -3,9 +3,10 @@ import Models
|
|||
import Network
|
||||
import Status
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
@MainActor
|
||||
class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||
@Observable class AccountDetailViewModel: StatusesFetcher {
|
||||
let accountId: String
|
||||
var client: Client?
|
||||
var isCurrentUser: Bool = false
|
||||
|
@ -56,8 +57,8 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
case lists
|
||||
}
|
||||
|
||||
@Published var accountState: AccountState = .loading
|
||||
@Published var tabState: TabState = .statuses(statusesState: .loading) {
|
||||
var accountState: AccountState = .loading
|
||||
var tabState: TabState = .statuses(statusesState: .loading) {
|
||||
didSet {
|
||||
/// Forward viewModel tabState related to statusesState to statusesState property
|
||||
/// for `StatusesFetcher` conformance as we wrap StatusesState in TabState
|
||||
|
@ -70,18 +71,18 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
}
|
||||
}
|
||||
|
||||
@Published var statusesState: StatusesState = .loading
|
||||
var statusesState: StatusesState = .loading
|
||||
|
||||
@Published var relationship: Relationship?
|
||||
@Published var pinned: [Status] = []
|
||||
@Published var favorites: [Status] = []
|
||||
@Published var bookmarks: [Status] = []
|
||||
var relationship: Relationship?
|
||||
var pinned: [Status] = []
|
||||
var favorites: [Status] = []
|
||||
var bookmarks: [Status] = []
|
||||
private var favoritesNextPage: LinkHandler?
|
||||
private var bookmarksNextPage: LinkHandler?
|
||||
@Published var featuredTags: [FeaturedTag] = []
|
||||
@Published var fields: [Account.Field] = []
|
||||
@Published var familiarFollowers: [Account] = []
|
||||
@Published var selectedTab = Tab.statuses {
|
||||
var featuredTags: [FeaturedTag] = []
|
||||
var fields: [Account.Field] = []
|
||||
var familiarFollowers: [Account] = []
|
||||
var selectedTab = Tab.statuses {
|
||||
didSet {
|
||||
switch selectedTab {
|
||||
case .statuses, .postsAndReplies, .media:
|
||||
|
@ -95,8 +96,8 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
}
|
||||
}
|
||||
|
||||
@Published var translation: Translation?
|
||||
@Published var isLoadingTranslation = false
|
||||
var translation: Translation?
|
||||
var isLoadingTranslation = false
|
||||
|
||||
private(set) var account: Account?
|
||||
private var tabTask: Task<Void, Never>?
|
||||
|
|
|
@ -5,13 +5,14 @@ import Env
|
|||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
@MainActor
|
||||
public class AccountsListRowViewModel: ObservableObject {
|
||||
@Observable public class AccountsListRowViewModel {
|
||||
var client: Client?
|
||||
|
||||
@Published var account: Account
|
||||
@Published var relationShip: Relationship?
|
||||
var account: Account
|
||||
var relationShip: Relationship?
|
||||
|
||||
public init(account: Account, relationShip: Relationship? = nil) {
|
||||
self.account = account
|
||||
|
@ -23,9 +24,9 @@ public struct AccountsListRow: View {
|
|||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
|
||||
@StateObject var viewModel: AccountsListRowViewModel
|
||||
@State var viewModel: AccountsListRowViewModel
|
||||
|
||||
@State private var isEditingRelationshipNote: Bool = false
|
||||
|
||||
|
@ -33,7 +34,7 @@ public struct AccountsListRow: View {
|
|||
let requestUpdated: (() -> Void)?
|
||||
|
||||
public init(viewModel: AccountsListRowViewModel, isFollowRequest: Bool = false, requestUpdated: (() -> Void)? = nil) {
|
||||
_viewModel = StateObject(wrappedValue: viewModel)
|
||||
self.viewModel = viewModel
|
||||
self.isFollowRequest = isFollowRequest
|
||||
self.requestUpdated = requestUpdated
|
||||
}
|
||||
|
@ -118,7 +119,7 @@ public struct AccountsListRow: View {
|
|||
.background(theme.primaryBackgroundColor)
|
||||
.environmentObject(theme)
|
||||
.environmentObject(currentAccount)
|
||||
.environmentObject(client)
|
||||
.environment(client)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@ import SwiftUI
|
|||
|
||||
public struct AccountsListView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@StateObject private var viewModel: AccountsListViewModel
|
||||
@State private var viewModel: AccountsListViewModel
|
||||
@State private var didAppear: Bool = false
|
||||
|
||||
public init(mode: AccountsListMode) {
|
||||
_viewModel = StateObject(wrappedValue: .init(mode: mode))
|
||||
_viewModel = .init(initialValue: .init(mode: mode))
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
public enum AccountsListMode {
|
||||
case following(accountId: String), followers(accountId: String)
|
||||
|
@ -24,7 +25,7 @@ public enum AccountsListMode {
|
|||
}
|
||||
|
||||
@MainActor
|
||||
class AccountsListViewModel: ObservableObject {
|
||||
@Observable class AccountsListViewModel {
|
||||
var client: Client?
|
||||
|
||||
let mode: AccountsListMode
|
||||
|
@ -44,7 +45,7 @@ class AccountsListViewModel: ObservableObject {
|
|||
private var accounts: [Account] = []
|
||||
private var relationships: [Relationship] = []
|
||||
|
||||
@Published var state = State.loading
|
||||
var state = State.loading
|
||||
|
||||
private var nextPageId: String?
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@ import SwiftUI
|
|||
|
||||
public struct EditAccountView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
@StateObject private var viewModel = EditAccountViewModel()
|
||||
@State private var viewModel = EditAccountViewModel()
|
||||
|
||||
public init() {}
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
@MainActor
|
||||
class EditAccountViewModel: ObservableObject {
|
||||
class FieldEditViewModel: ObservableObject, Identifiable {
|
||||
@Observable class EditAccountViewModel {
|
||||
@Observable class FieldEditViewModel: Identifiable {
|
||||
let id = UUID().uuidString
|
||||
@Published var name: String = ""
|
||||
@Published var value: String = ""
|
||||
var name: String = ""
|
||||
var value: String = ""
|
||||
|
||||
init(name: String, value: String) {
|
||||
self.name = name
|
||||
|
@ -17,18 +18,18 @@ class EditAccountViewModel: ObservableObject {
|
|||
|
||||
public var client: Client?
|
||||
|
||||
@Published var displayName: String = ""
|
||||
@Published var note: String = ""
|
||||
@Published var postPrivacy = Models.Visibility.pub
|
||||
@Published var isSensitive: Bool = false
|
||||
@Published var isBot: Bool = false
|
||||
@Published var isLocked: Bool = false
|
||||
@Published var isDiscoverable: Bool = false
|
||||
@Published var fields: [FieldEditViewModel] = []
|
||||
var displayName: String = ""
|
||||
var note: String = ""
|
||||
var postPrivacy = Models.Visibility.pub
|
||||
var isSensitive: Bool = false
|
||||
var isBot: Bool = false
|
||||
var isLocked: Bool = false
|
||||
var isDiscoverable: Bool = false
|
||||
var fields: [FieldEditViewModel] = []
|
||||
|
||||
@Published var isLoading: Bool = true
|
||||
@Published var isSaving: Bool = false
|
||||
@Published var saveError: Bool = false
|
||||
var isLoading: Bool = true
|
||||
var isSaving: Bool = false
|
||||
var saveError: Bool = false
|
||||
|
||||
init() {}
|
||||
|
||||
|
|
|
@ -5,12 +5,10 @@ import SwiftUI
|
|||
public struct EditRelationshipNoteView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
|
||||
// need this model to refresh after storing the new note on mastodon
|
||||
var accountDetailViewModel: AccountDetailViewModel
|
||||
|
||||
@StateObject private var viewModel = EditRelationshipNoteViewModel()
|
||||
@State var accountDetailViewModel: AccountDetailViewModel
|
||||
@State private var viewModel = EditRelationshipNoteViewModel()
|
||||
|
||||
public var body: some View {
|
||||
NavigationStack {
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import Network
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
@MainActor
|
||||
class EditRelationshipNoteViewModel: ObservableObject {
|
||||
@Observable class EditRelationshipNoteViewModel {
|
||||
public var note: String = ""
|
||||
public var relatedAccountId: String?
|
||||
public var client: Client?
|
||||
|
||||
@Published var isSaving: Bool = false
|
||||
@Published var saveError: Bool = false
|
||||
var isSaving: Bool = false
|
||||
var saveError: Bool = false
|
||||
|
||||
init() {}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ struct EditFilterView: View {
|
|||
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var account: CurrentAccount
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
|
||||
@State private var isSavingFilter: Bool = false
|
||||
@State private var filter: ServerFilter?
|
||||
|
@ -91,9 +91,9 @@ struct EditFilterView: View {
|
|||
Text(duration.description).tag(duration)
|
||||
}
|
||||
}
|
||||
.onChange(of: expirySelection) { duration in
|
||||
if duration != .custom {
|
||||
expiresAt = Date(timeIntervalSinceNow: TimeInterval(duration.rawValue))
|
||||
.onChange(of: expirySelection) { oldValue, newValue in
|
||||
if newValue != .custom {
|
||||
expiresAt = Date(timeIntervalSinceNow: TimeInterval(newValue.rawValue))
|
||||
}
|
||||
}
|
||||
if expirySelection != .infinite {
|
||||
|
@ -227,7 +227,7 @@ struct EditFilterView: View {
|
|||
} label: {
|
||||
EmptyView()
|
||||
}
|
||||
.onChange(of: filterAction) { _ in
|
||||
.onChange(of: filterAction) {
|
||||
Task {
|
||||
await saveFilter()
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ public struct FiltersListView: View {
|
|||
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var account: CurrentAccount
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
|
||||
@State private var isLoading: Bool = true
|
||||
@State private var filters: [ServerFilter] = []
|
||||
|
|
|
@ -3,16 +3,17 @@ import Foundation
|
|||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
@MainActor
|
||||
public class FollowButtonViewModel: ObservableObject {
|
||||
@Observable public class FollowButtonViewModel {
|
||||
var client: Client?
|
||||
|
||||
public let accountId: String
|
||||
public let shouldDisplayNotify: Bool
|
||||
public let relationshipUpdated: (Relationship) -> Void
|
||||
@Published public private(set) var relationship: Relationship
|
||||
@Published public private(set) var isUpdating: Bool = false
|
||||
public private(set) var relationship: Relationship
|
||||
public private(set) var isUpdating: Bool = false
|
||||
|
||||
public init(accountId: String,
|
||||
relationship: Relationship,
|
||||
|
@ -75,11 +76,11 @@ public class FollowButtonViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
public struct FollowButton: View {
|
||||
@EnvironmentObject private var client: Client
|
||||
@StateObject private var viewModel: FollowButtonViewModel
|
||||
@Environment(Client.self) private var client
|
||||
@State private var viewModel: FollowButtonViewModel
|
||||
|
||||
public init(viewModel: FollowButtonViewModel) {
|
||||
_viewModel = StateObject(wrappedValue: viewModel)
|
||||
_viewModel = .init(initialValue: viewModel)
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
|
|
|
@ -7,7 +7,7 @@ let package = Package(
|
|||
name: "AppAccount",
|
||||
defaultLocalization: "en",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.iOS(.v17),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
|
|
@ -6,13 +6,13 @@ import SwiftUI
|
|||
public struct AppAccountView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
@EnvironmentObject private var appAccounts: AppAccountsManager
|
||||
@Environment(AppAccountsManager.self) private var appAccounts
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
|
||||
@StateObject var viewModel: AppAccountViewModel
|
||||
@State var viewModel: AppAccountViewModel
|
||||
|
||||
public init(viewModel: AppAccountViewModel) {
|
||||
_viewModel = .init(wrappedValue: viewModel)
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
|
|
|
@ -3,9 +3,10 @@ import DesignSystem
|
|||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
@MainActor
|
||||
public class AppAccountViewModel: ObservableObject {
|
||||
@Observable public class AppAccountViewModel {
|
||||
private static var avatarsCache: [String: UIImage] = [:]
|
||||
private static var accountsCache: [String: Account] = [:]
|
||||
|
||||
|
@ -15,7 +16,7 @@ public class AppAccountViewModel: ObservableObject {
|
|||
let isInNavigation: Bool
|
||||
let showBadge: Bool
|
||||
|
||||
@Published var account: Account? {
|
||||
var account: Account? {
|
||||
didSet {
|
||||
if let account {
|
||||
refreshAcct(account: account)
|
||||
|
|
|
@ -3,13 +3,14 @@ import Env
|
|||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
@MainActor
|
||||
public class AppAccountsManager: ObservableObject {
|
||||
@Observable public class AppAccountsManager {
|
||||
@AppStorage("latestCurrentAccountKey", store: UserPreferences.sharedDefault)
|
||||
public static var latestCurrentAccountKey: String = ""
|
||||
|
||||
@Published public var currentAccount: AppAccount {
|
||||
public var currentAccount: AppAccount {
|
||||
didSet {
|
||||
Self.latestCurrentAccountKey = currentAccount.id
|
||||
currentClient = .init(server: currentAccount.server,
|
||||
|
@ -17,8 +18,8 @@ public class AppAccountsManager: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
@Published public var availableAccounts: [AppAccount]
|
||||
@Published public var currentClient: Client
|
||||
public var availableAccounts: [AppAccount]
|
||||
public var currentClient: Client
|
||||
|
||||
public var pushAccounts: [PushAccount] {
|
||||
availableAccounts.filter { $0.oauthToken != nil }
|
||||
|
|
|
@ -5,7 +5,7 @@ import SwiftUI
|
|||
public struct AppAccountsSelectorView: View {
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var appAccounts: AppAccountsManager
|
||||
@Environment(AppAccountsManager.self) private var appAccounts
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
@ObservedObject var routerPath: RouterPath
|
||||
|
@ -61,7 +61,7 @@ public struct AppAccountsSelectorView: View {
|
|||
}
|
||||
}
|
||||
})
|
||||
.onChange(of: currentAccount.account?.id) { _ in
|
||||
.onChange(of: currentAccount.account?.id) {
|
||||
refreshAccounts()
|
||||
}
|
||||
.onAppear {
|
||||
|
|
|
@ -7,7 +7,7 @@ let package = Package(
|
|||
name: "Conversations",
|
||||
defaultLocalization: "en",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.iOS(.v17),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
|
|
@ -13,7 +13,7 @@ public struct ConversationDetailView: View {
|
|||
@EnvironmentObject private var quickLook: QuickLook
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var watcher: StreamWatcher
|
||||
|
||||
|
@ -85,7 +85,7 @@ public struct ConversationDetailView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: watcher.latestEvent?.id) { _ in
|
||||
.onChange(of: watcher.latestEvent?.id) {
|
||||
if let latestEvent = watcher.latestEvent {
|
||||
viewModel.handleEvent(event: latestEvent)
|
||||
DispatchQueue.main.async {
|
||||
|
|
|
@ -9,7 +9,7 @@ struct ConversationMessageView: View {
|
|||
@EnvironmentObject private var quickLook: QuickLook
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
let message: Status
|
||||
|
|
|
@ -6,7 +6,7 @@ import Network
|
|||
import SwiftUI
|
||||
|
||||
struct ConversationsListRow: View {
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
|
|
|
@ -9,7 +9,7 @@ public struct ConversationsListView: View {
|
|||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
@EnvironmentObject private var watcher: StreamWatcher
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
@StateObject private var viewModel = ConversationsListViewModel()
|
||||
|
@ -83,7 +83,7 @@ public struct ConversationsListView: View {
|
|||
SecondaryColumnToolbarItem()
|
||||
}
|
||||
}
|
||||
.onChange(of: watcher.latestEvent?.id) { _ in
|
||||
.onChange(of: watcher.latestEvent?.id) {
|
||||
if let latestEvent = watcher.latestEvent {
|
||||
viewModel.handleEvent(event: latestEvent)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ let package = Package(
|
|||
name: "DesignSystem",
|
||||
defaultLocalization: "en",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.iOS(.v17),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
|
|
@ -41,16 +41,16 @@ struct ThemeApplier: ViewModifier {
|
|||
setWindowUserInterfaceStyle(from: theme.selectedScheme)
|
||||
setBarsColor(theme.primaryBackgroundColor)
|
||||
}
|
||||
.onChange(of: theme.tintColor) { newValue in
|
||||
.onChange(of: theme.tintColor) { oldValue, newValue in
|
||||
setWindowTint(newValue)
|
||||
}
|
||||
.onChange(of: theme.primaryBackgroundColor) { newValue in
|
||||
.onChange(of: theme.primaryBackgroundColor) { oldValue, newValue in
|
||||
setBarsColor(newValue)
|
||||
}
|
||||
.onChange(of: theme.selectedScheme) { newValue in
|
||||
.onChange(of: theme.selectedScheme) { oldValue, newValue in
|
||||
setWindowUserInterfaceStyle(from: newValue)
|
||||
}
|
||||
.onChange(of: colorScheme) { newColorScheme in
|
||||
.onChange(of: colorScheme) { oldValue, newColorScheme in
|
||||
if theme.followSystemColorScheme,
|
||||
let sets = availableColorsSets
|
||||
.first(where: { $0.light.name == theme.selectedSet || $0.dark.name == theme.selectedSet })
|
||||
|
|
|
@ -81,7 +81,7 @@ struct ThemeBoxView: View {
|
|||
.onAppear {
|
||||
isSelected = theme.selectedSet.rawValue == color.name.rawValue
|
||||
}
|
||||
.onChange(of: theme.selectedSet) { newValue in
|
||||
.onChange(of: theme.selectedSet) { oldValue, newValue in
|
||||
isSelected = newValue.rawValue == color.name.rawValue
|
||||
}
|
||||
.onTapGesture {
|
||||
|
|
|
@ -7,7 +7,7 @@ let package = Package(
|
|||
name: "Env",
|
||||
defaultLocalization: "en",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.iOS(.v17),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
|
|
@ -7,7 +7,7 @@ let package = Package(
|
|||
name: "Explore",
|
||||
defaultLocalization: "en",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.iOS(.v17),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
|
|
@ -9,7 +9,7 @@ import SwiftUI
|
|||
|
||||
public struct ExploreView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
|
||||
@StateObject private var viewModel = ExploreViewModel()
|
||||
|
@ -117,7 +117,7 @@ public struct ExploreView: View {
|
|||
|
||||
private var loadingView: some View {
|
||||
ForEach(Status.placeholders()) { status in
|
||||
StatusRowView(viewModel: { .init(status: status, client: client, routerPath: routerPath) })
|
||||
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
|
||||
.padding(.vertical, 8)
|
||||
.redacted(reason: .placeholder)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
@ -148,7 +148,7 @@ public struct ExploreView: View {
|
|||
if !results.statuses.isEmpty, viewModel.searchScope == .all || viewModel.searchScope == .posts {
|
||||
Section("explore.section.posts") {
|
||||
ForEach(results.statuses) { status in
|
||||
StatusRowView(viewModel: { .init(status: status, client: client, routerPath: routerPath) })
|
||||
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ public struct ExploreView: View {
|
|||
ForEach(viewModel.trendingStatuses
|
||||
.prefix(upTo: viewModel.trendingStatuses.count > 3 ? 3 : viewModel.trendingStatuses.count))
|
||||
{ status in
|
||||
StatusRowView(viewModel: { .init(status: status, client: client, routerPath: routerPath) })
|
||||
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ let package = Package(
|
|||
name: "Lists",
|
||||
defaultLocalization: "en",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.iOS(.v17),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
|
|
@ -6,7 +6,7 @@ import SwiftUI
|
|||
|
||||
public struct ListAddAccountView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@StateObject private var viewModel: ListAddAccountViewModel
|
||||
|
|
|
@ -7,7 +7,7 @@ import SwiftUI
|
|||
public struct ListEditView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
|
||||
@StateObject private var viewModel: ListEditViewModel
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ let package = Package(
|
|||
name: "Models",
|
||||
defaultLocalization: "en",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.iOS(.v17),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
|
|
@ -7,7 +7,7 @@ let package = Package(
|
|||
name: "Network",
|
||||
defaultLocalization: "en",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.iOS(.v17),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
|
|
@ -3,8 +3,9 @@ import Foundation
|
|||
import Models
|
||||
import os
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
public final class Client: ObservableObject, Equatable, Identifiable, Hashable {
|
||||
@Observable public final class Client: Equatable, Identifiable, Hashable {
|
||||
public static func == (lhs: Client, rhs: Client) -> Bool {
|
||||
let lhsToken = lhs.critical.withLock { $0.oauthToken }
|
||||
let rhsToken = rhs.critical.withLock { $0.oauthToken }
|
||||
|
|
|
@ -7,7 +7,7 @@ let package = Package(
|
|||
name: "Notifications",
|
||||
defaultLocalization: "en",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.iOS(.v17),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
|
|
@ -149,16 +149,16 @@ struct NotificationRowView: View {
|
|||
if let status = notification.status {
|
||||
HStack {
|
||||
if type == .mention {
|
||||
StatusRowView(viewModel: { .init(status: status,
|
||||
client: client,
|
||||
routerPath: routerPath,
|
||||
showActions: true) })
|
||||
StatusRowView(viewModel: .init(status: status,
|
||||
client: client,
|
||||
routerPath: routerPath,
|
||||
showActions: true))
|
||||
} else {
|
||||
StatusRowView(viewModel: { .init(status: status,
|
||||
client: client,
|
||||
routerPath: routerPath,
|
||||
showActions: false,
|
||||
textDisabled: true) })
|
||||
StatusRowView(viewModel: .init(status: status,
|
||||
client: client,
|
||||
routerPath: routerPath,
|
||||
showActions: false,
|
||||
textDisabled: true))
|
||||
.lineLimit(4)
|
||||
}
|
||||
Spacer()
|
||||
|
|
|
@ -9,7 +9,7 @@ public struct NotificationsListView: View {
|
|||
@Environment(\.scenePhase) private var scenePhase
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var watcher: StreamWatcher
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
@EnvironmentObject private var account: CurrentAccount
|
||||
@StateObject private var viewModel = NotificationsViewModel()
|
||||
|
@ -88,13 +88,13 @@ public struct NotificationsListView: View {
|
|||
HapticManager.shared.fireHaptic(of: .dataRefresh(intensity: 0.7))
|
||||
SoundEffectManager.shared.playSound(of: .refresh)
|
||||
}
|
||||
.onChange(of: watcher.latestEvent?.id, perform: { _ in
|
||||
.onChange(of: watcher.latestEvent?.id) {
|
||||
if let latestEvent = watcher.latestEvent {
|
||||
viewModel.handleEvent(event: latestEvent)
|
||||
}
|
||||
})
|
||||
.onChange(of: scenePhase, perform: { scenePhase in
|
||||
switch scenePhase {
|
||||
}
|
||||
.onChange(of: scenePhase) { oldValue, newValue in
|
||||
switch newValue {
|
||||
case .active:
|
||||
Task {
|
||||
await viewModel.fetchNotifications()
|
||||
|
@ -102,7 +102,7 @@ public struct NotificationsListView: View {
|
|||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
|
|
@ -7,7 +7,7 @@ let package = Package(
|
|||
name: "Status",
|
||||
defaultLocalization: "en",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.iOS(.v17),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
|
|
@ -9,7 +9,7 @@ public struct StatusDetailView: View {
|
|||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var watcher: StreamWatcher
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
|
||||
@StateObject private var viewModel: StatusDetailViewModel
|
||||
|
@ -69,10 +69,10 @@ public struct StatusDetailView: View {
|
|||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(theme.primaryBackgroundColor)
|
||||
.onChange(of: viewModel.scrollToId, perform: { scrollToId in
|
||||
if let scrollToId {
|
||||
.onChange(of: viewModel.scrollToId, { oldValue, newValue in
|
||||
if let newValue {
|
||||
viewModel.scrollToId = nil
|
||||
proxy.scrollTo(scrollToId, anchor: .top)
|
||||
proxy.scrollTo(newValue, anchor: .top)
|
||||
}
|
||||
})
|
||||
.task {
|
||||
|
@ -92,7 +92,7 @@ public struct StatusDetailView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: watcher.latestEvent?.id) { _ in
|
||||
.onChange(of: watcher.latestEvent?.id) {
|
||||
guard let lastEvent = watcher.latestEvent else { return }
|
||||
viewModel.handleEvent(event: lastEvent, currentAccount: currentAccount.account)
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ public struct StatusDetailView: View {
|
|||
makeCurrentStatusView(status: status)
|
||||
.environment(\.extraLeadingInset, isReplyToPrevious ? 10 : 0)
|
||||
} else {
|
||||
StatusRowView(viewModel: { viewModel })
|
||||
StatusRowView(viewModel: viewModel)
|
||||
.environment(\.extraLeadingInset, isReplyToPrevious ? 10 : 0)
|
||||
}
|
||||
}
|
||||
|
@ -145,9 +145,9 @@ public struct StatusDetailView: View {
|
|||
}
|
||||
|
||||
private func makeCurrentStatusView(status: Status) -> some View {
|
||||
StatusRowView(viewModel: { .init(status: status,
|
||||
client: client,
|
||||
routerPath: routerPath) })
|
||||
StatusRowView(viewModel: .init(status: status,
|
||||
client: client,
|
||||
routerPath: routerPath))
|
||||
.environment(\.isStatusFocused, true)
|
||||
.environment(\.isStatusDetailLoaded, !viewModel.isLoadingContext)
|
||||
.accessibilityFocused($initialFocusBugWorkaround, equals: true)
|
||||
|
@ -181,7 +181,7 @@ public struct StatusDetailView: View {
|
|||
|
||||
private var loadingDetailView: some View {
|
||||
ForEach(Status.placeholders()) { status in
|
||||
StatusRowView(viewModel: { .init(status: status, client: client, routerPath: routerPath) })
|
||||
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
|
||||
.redacted(reason: .placeholder)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,10 +38,6 @@ struct StatusEditorPollView: View {
|
|||
addChoice(at: index)
|
||||
}
|
||||
}
|
||||
.onChange(of: viewModel.pollOptions[index]) {
|
||||
let maxCharacters: Int = currentInstance.instance?.configuration?.polls.maxCharactersPerOption ?? 50
|
||||
viewModel.pollOptions[index] = String($0.prefix(maxCharacters))
|
||||
}
|
||||
|
||||
if canAddMoreAt(index) {
|
||||
Button {
|
||||
|
|
|
@ -12,10 +12,10 @@ import SwiftUI
|
|||
import UIKit
|
||||
|
||||
public struct StatusEditorView: View {
|
||||
@EnvironmentObject private var appAccounts: AppAccountsManager
|
||||
@Environment(AppAccountsManager.self) private var appAccounts
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
@ -90,9 +90,9 @@ public struct StatusEditorView: View {
|
|||
await viewModel.fetchCustomEmojis()
|
||||
}
|
||||
}
|
||||
.onChange(of: currentAccount.account?.id, perform: { _ in
|
||||
.onChange(of: currentAccount.account?.id) {
|
||||
viewModel.currentAccount = currentAccount.account
|
||||
})
|
||||
}
|
||||
.background(theme.primaryBackgroundColor)
|
||||
.navigationTitle(viewModel.mode.title)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
|
@ -161,10 +161,10 @@ public struct StatusEditorView: View {
|
|||
}
|
||||
}
|
||||
.interactiveDismissDisabled(viewModel.shouldDisplayDismissWarning)
|
||||
.onChange(of: appAccounts.currentClient) { newClient in
|
||||
.onChange(of: appAccounts.currentClient) { oldValue, newValue in
|
||||
if viewModel.mode.isInShareExtension {
|
||||
currentAccount.setClient(client: newClient)
|
||||
viewModel.client = newClient
|
||||
currentAccount.setClient(client: newValue)
|
||||
viewModel.client = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,10 @@ public struct StatusEmbeddedView: View {
|
|||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
makeAccountView(account: status.reblog?.account ?? status.account)
|
||||
StatusRowView(viewModel: { .init(status: status,
|
||||
client: client,
|
||||
routerPath: routerPath,
|
||||
showActions: false) })
|
||||
StatusRowView(viewModel: .init(status: status,
|
||||
client: client,
|
||||
routerPath: routerPath,
|
||||
showActions: false))
|
||||
.accessibilityLabel(status.content.asRawText)
|
||||
.environment(\.isCompact, true)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import SwiftUI
|
|||
public struct StatusEditHistoryView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
private let statusId: String
|
||||
|
|
|
@ -29,7 +29,7 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
|
|||
switch fetcher.statusesState {
|
||||
case .loading:
|
||||
ForEach(Status.placeholders()) { status in
|
||||
StatusRowView(viewModel: { .init(status: status, client: client, routerPath: routerPath) })
|
||||
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
|
||||
.redacted(reason: .placeholder)
|
||||
}
|
||||
case .error:
|
||||
|
@ -46,12 +46,10 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
|
|||
|
||||
case let .display(statuses, nextPageState):
|
||||
ForEach(statuses, id: \.viewId) { status in
|
||||
StatusRowView(viewModel: { StatusRowViewModel(status: status,
|
||||
client: client,
|
||||
routerPath: routerPath,
|
||||
isRemote: isRemote)
|
||||
|
||||
})
|
||||
StatusRowView(viewModel: StatusRowViewModel(status: status,
|
||||
client: client,
|
||||
routerPath: routerPath,
|
||||
isRemote: isRemote))
|
||||
.id(status.id)
|
||||
.onAppear {
|
||||
fetcher.statusDidAppear(status: status)
|
||||
|
|
|
@ -2,10 +2,11 @@ import AVKit
|
|||
import DesignSystem
|
||||
import Env
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
@MainActor
|
||||
class VideoPlayerViewModel: ObservableObject {
|
||||
@Published var player: AVPlayer?
|
||||
@Observable class VideoPlayerViewModel {
|
||||
var player: AVPlayer?
|
||||
private let url: URL
|
||||
|
||||
init(url: URL) {
|
||||
|
@ -53,7 +54,7 @@ struct VideoPlayerView: View {
|
|||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
@StateObject var viewModel: VideoPlayerViewModel
|
||||
@State var viewModel: VideoPlayerViewModel
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
|
@ -75,8 +76,8 @@ struct VideoPlayerView: View {
|
|||
viewModel.pause()
|
||||
}
|
||||
.cornerRadius(4)
|
||||
.onChange(of: scenePhase, perform: { scenePhase in
|
||||
switch scenePhase {
|
||||
.onChange(of: scenePhase) { oldValue, newValue in
|
||||
switch newValue {
|
||||
case .background, .inactive:
|
||||
viewModel.pause()
|
||||
case .active:
|
||||
|
@ -86,6 +87,6 @@ struct VideoPlayerView: View {
|
|||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,15 +6,16 @@ import SwiftUI
|
|||
|
||||
public struct StatusPollView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var currentInstance: CurrentInstance
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@StateObject private var viewModel: StatusPollViewModel
|
||||
|
||||
@State private var viewModel: StatusPollViewModel
|
||||
|
||||
private var status: AnyStatus
|
||||
|
||||
public init(poll: Poll, status: AnyStatus) {
|
||||
_viewModel = StateObject(wrappedValue: .init(poll: poll))
|
||||
_viewModel = .init(initialValue: .init(poll: poll))
|
||||
self.status = status
|
||||
}
|
||||
|
||||
|
|
|
@ -2,14 +2,15 @@ import Combine
|
|||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
@MainActor
|
||||
public class StatusPollViewModel: ObservableObject {
|
||||
@Observable public class StatusPollViewModel {
|
||||
public var client: Client?
|
||||
public var instance: Instance?
|
||||
|
||||
@Published var poll: Poll
|
||||
@Published var votes: [Int] = []
|
||||
var poll: Poll
|
||||
var votes: [Int] = []
|
||||
|
||||
var showResults: Bool {
|
||||
poll.ownVotes?.isEmpty == false || poll.expired
|
||||
|
|
|
@ -28,8 +28,8 @@ struct StatusActionButtonStyle: ButtonStyle {
|
|||
SparklesView(counter: sparklesCounter, tint: tint, size: 5, velocity: 30)
|
||||
}
|
||||
}
|
||||
.onChange(of: configuration.isPressed) { isPressed in
|
||||
guard tintColor != nil, !isPressed, !isOn else { return }
|
||||
.onChange(of: configuration.isPressed) { oldValue, newValue in
|
||||
guard tintColor != nil, !newValue, !isOn else { return }
|
||||
|
||||
withAnimation(.spring(response: 1, dampingFraction: 1)) {
|
||||
sparklesCounter += 1
|
||||
|
@ -88,8 +88,8 @@ struct StatusActionButtonStyle: ButtonStyle {
|
|||
.onAppear {
|
||||
cells = Self.generateCells()
|
||||
}
|
||||
.onChange(of: counter) { [counter] newCounter in
|
||||
if floor(counter) != floor(newCounter) {
|
||||
.onChange(of: counter) { oldValue, newValue in
|
||||
if floor(oldValue) != floor(newValue) {
|
||||
cells = Self.generateCells()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,10 @@ public struct StatusRowView: View {
|
|||
@EnvironmentObject private var quickLook: QuickLook
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
@StateObject var viewModel: StatusRowViewModel
|
||||
@State var viewModel: StatusRowViewModel
|
||||
|
||||
// StateObject accepts an @autoclosure which only allocates the view model once when the view gets on screen.
|
||||
public init(viewModel: @escaping () -> StatusRowViewModel) {
|
||||
_viewModel = StateObject(wrappedValue: viewModel())
|
||||
public init(viewModel: StatusRowViewModel) {
|
||||
_viewModel = .init(initialValue: viewModel)
|
||||
}
|
||||
|
||||
var contextMenu: some View {
|
||||
|
|
|
@ -5,9 +5,10 @@ import Models
|
|||
import NaturalLanguage
|
||||
import Network
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
@MainActor
|
||||
public class StatusRowViewModel: ObservableObject {
|
||||
@Observable public class StatusRowViewModel {
|
||||
let status: Status
|
||||
// Whether this status is on a remote local timeline (many actions are unavailable if so)
|
||||
let isRemote: Bool
|
||||
|
@ -15,26 +16,26 @@ public class StatusRowViewModel: ObservableObject {
|
|||
let textDisabled: Bool
|
||||
let finalStatus: AnyStatus
|
||||
|
||||
@Published var isPinned: Bool
|
||||
@Published var embeddedStatus: Status?
|
||||
@Published var displaySpoiler: Bool = false
|
||||
@Published var isEmbedLoading: Bool = false
|
||||
@Published var isFiltered: Bool = false
|
||||
var isPinned: Bool
|
||||
var embeddedStatus: Status?
|
||||
var displaySpoiler: Bool = false
|
||||
var isEmbedLoading: Bool = false
|
||||
var isFiltered: Bool = false
|
||||
|
||||
@Published var translation: Translation?
|
||||
@Published var isLoadingTranslation: Bool = false
|
||||
@Published var showDeleteAlert: Bool = false
|
||||
var translation: Translation?
|
||||
var isLoadingTranslation: Bool = false
|
||||
var showDeleteAlert: Bool = false
|
||||
|
||||
private var actionsAccountsFetched: Bool = false
|
||||
@Published var favoriters: [Account] = []
|
||||
@Published var rebloggers: [Account] = []
|
||||
var favoriters: [Account] = []
|
||||
var rebloggers: [Account] = []
|
||||
|
||||
@Published var isLoadingRemoteContent: Bool = false
|
||||
@Published var localStatusId: String?
|
||||
@Published var localStatus: Status?
|
||||
var isLoadingRemoteContent: Bool = false
|
||||
var localStatusId: String?
|
||||
var localStatus: Status?
|
||||
|
||||
// The relationship our user has to the author of this post, if available
|
||||
@Published var authorRelationship: Relationship? {
|
||||
var authorRelationship: Relationship? {
|
||||
didSet {
|
||||
// if we are newly blocking or muting the author, force collapse post so it goes away
|
||||
if let relationship = authorRelationship,
|
||||
|
@ -46,14 +47,14 @@ public class StatusRowViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
// used by the button to expand a collapsed post
|
||||
@Published var isCollapsed: Bool = true {
|
||||
var isCollapsed: Bool = true {
|
||||
didSet {
|
||||
recalcCollapse()
|
||||
}
|
||||
}
|
||||
|
||||
// number of lines to show, nil means show the whole post
|
||||
@Published var lineLimit: Int? = nil
|
||||
var lineLimit: Int? = nil
|
||||
// post length determining if the post should be collapsed
|
||||
let collapseThresholdLength: Int = 750
|
||||
// number of text lines to show on a collpased post
|
||||
|
|
|
@ -13,7 +13,7 @@ struct StatusRowActionsView: View {
|
|||
@Environment(\.isStatusFocused) private var isFocused
|
||||
@Environment(\.isStatusDetailLoaded) private var isStatusDetailLoaded
|
||||
|
||||
@ObservedObject var viewModel: StatusRowViewModel
|
||||
var viewModel: StatusRowViewModel
|
||||
|
||||
|
||||
func privateBoost() -> Bool {
|
||||
|
|
|
@ -10,10 +10,11 @@ struct StatusRowContentView: View {
|
|||
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
@ObservedObject var viewModel: StatusRowViewModel
|
||||
var viewModel: StatusRowViewModel
|
||||
|
||||
var body: some View {
|
||||
if !viewModel.finalStatus.spoilerText.asRawText.isEmpty {
|
||||
@Bindable var viewModel = viewModel
|
||||
StatusRowSpoilerView(status: viewModel.finalStatus, displaySpoiler: $viewModel.displaySpoiler)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,14 +7,14 @@ import SwiftUI
|
|||
struct StatusRowContextMenu: View {
|
||||
@Environment(\.displayScale) var displayScale
|
||||
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var sceneDelegate: SceneDelegate
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var account: CurrentAccount
|
||||
@EnvironmentObject private var currentInstance: CurrentInstance
|
||||
@EnvironmentObject private var statusDataController: StatusDataController
|
||||
|
||||
@ObservedObject var viewModel: StatusRowViewModel
|
||||
var viewModel: StatusRowViewModel
|
||||
|
||||
var boostLabel: some View {
|
||||
if viewModel.status.visibility == .priv, viewModel.status.account.id == account.account?.id {
|
||||
|
@ -81,7 +81,7 @@ struct StatusRowContextMenu: View {
|
|||
|
||||
Button {
|
||||
let view = HStack {
|
||||
StatusRowView(viewModel: { viewModel })
|
||||
StatusRowView(viewModel: viewModel)
|
||||
.padding(16)
|
||||
}
|
||||
.environment(\.isInCaptureMode, true)
|
||||
|
@ -91,7 +91,7 @@ struct StatusRowContextMenu: View {
|
|||
.environmentObject(currentInstance)
|
||||
.environmentObject(SceneDelegate())
|
||||
.environmentObject(QuickLook())
|
||||
.environmentObject(viewModel.client)
|
||||
.environment(viewModel.client)
|
||||
.preferredColorScheme(Theme.shared.selectedScheme == .dark ? .dark : .light)
|
||||
.foregroundColor(Theme.shared.labelColor)
|
||||
.background(Theme.shared.primaryBackgroundColor)
|
||||
|
|
|
@ -8,7 +8,7 @@ struct StatusRowDetailView: View {
|
|||
|
||||
@EnvironmentObject private var statusDataController: StatusDataController
|
||||
|
||||
@ObservedObject var viewModel: StatusRowViewModel
|
||||
var viewModel: StatusRowViewModel
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
|
|
|
@ -17,7 +17,7 @@ struct StatusRowSwipeView: View {
|
|||
viewModel.status.visibility == .priv && viewModel.status.account.id == currentAccount.account?.id
|
||||
}
|
||||
|
||||
@ObservedObject var viewModel: StatusRowViewModel
|
||||
var viewModel: StatusRowViewModel
|
||||
let mode: Mode
|
||||
|
||||
var body: some View {
|
||||
|
|
|
@ -7,7 +7,7 @@ struct StatusRowTextView: View {
|
|||
@EnvironmentObject private var theme: Theme
|
||||
@Environment(\.isStatusFocused) private var isFocused
|
||||
|
||||
@ObservedObject var viewModel: StatusRowViewModel
|
||||
var viewModel: StatusRowViewModel
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
|
|
|
@ -9,7 +9,7 @@ struct StatusRowTranslateView: View {
|
|||
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
|
||||
@ObservedObject var viewModel: StatusRowViewModel
|
||||
var viewModel: StatusRowViewModel
|
||||
|
||||
private var shouldShowTranslateButton: Bool {
|
||||
let statusLang = viewModel.getStatusLang()
|
||||
|
|
|
@ -7,7 +7,7 @@ let package = Package(
|
|||
name: "Timeline",
|
||||
defaultLocalization: "en",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.iOS(.v17),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
|
|
@ -2,10 +2,11 @@ import Env
|
|||
import Foundation
|
||||
import Models
|
||||
import SwiftUI
|
||||
import Observation
|
||||
|
||||
@MainActor
|
||||
class PendingStatusesObserver: ObservableObject {
|
||||
@Published var pendingStatusesCount: Int = 0
|
||||
@Observable class PendingStatusesObserver {
|
||||
var pendingStatusesCount: Int = 0
|
||||
|
||||
var disableUpdate: Bool = false
|
||||
var scrollToIndex: ((Int) -> Void)?
|
||||
|
@ -27,7 +28,7 @@ class PendingStatusesObserver: ObservableObject {
|
|||
}
|
||||
|
||||
struct PendingStatusesObserverView: View {
|
||||
@ObservedObject var observer: PendingStatusesObserver
|
||||
@State var observer: PendingStatusesObserver
|
||||
|
||||
var body: some View {
|
||||
if observer.pendingStatusesCount > 0 {
|
||||
|
|
|
@ -16,7 +16,7 @@ public struct TimelineView: View {
|
|||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var account: CurrentAccount
|
||||
@EnvironmentObject private var watcher: StreamWatcher
|
||||
@EnvironmentObject private var client: Client
|
||||
@Environment(Client.self) private var client
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
|
||||
@StateObject private var viewModel = TimelineViewModel()
|
||||
|
@ -58,7 +58,7 @@ public struct TimelineView: View {
|
|||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(theme.primaryBackgroundColor)
|
||||
.introspect(.list, on: .iOS(.v16, .v17)) { (collectionView: UICollectionView) in
|
||||
.introspect(.list, on: .iOS(.v17)) { (collectionView: UICollectionView) in
|
||||
DispatchQueue.main.async {
|
||||
self.collectionView = collectionView
|
||||
}
|
||||
|
@ -70,24 +70,24 @@ public struct TimelineView: View {
|
|||
PendingStatusesObserverView(observer: viewModel.pendingStatusesObserver)
|
||||
}
|
||||
}
|
||||
.onChange(of: viewModel.scrollToIndex) { index in
|
||||
.onChange(of: viewModel.scrollToIndex) { oldValue, newValue in
|
||||
if let collectionView,
|
||||
let index,
|
||||
let newValue,
|
||||
let rows = collectionView.dataSource?.collectionView(collectionView, numberOfItemsInSection: 0),
|
||||
rows > index
|
||||
rows > newValue
|
||||
{
|
||||
collectionView.scrollToItem(at: .init(row: index, section: 0),
|
||||
collectionView.scrollToItem(at: .init(row: newValue, section: 0),
|
||||
at: .top,
|
||||
animated: viewModel.scrollToIndexAnimated)
|
||||
viewModel.scrollToIndexAnimated = false
|
||||
viewModel.scrollToIndex = nil
|
||||
}
|
||||
}
|
||||
.onChange(of: scrollToTopSignal, perform: { _ in
|
||||
.onChange(of: scrollToTopSignal) {
|
||||
withAnimation {
|
||||
proxy.scrollTo(Constants.scrollToTop, anchor: .top)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
|
@ -145,25 +145,25 @@ public struct TimelineView: View {
|
|||
HapticManager.shared.fireHaptic(of: .dataRefresh(intensity: 0.7))
|
||||
SoundEffectManager.shared.playSound(of: .refresh)
|
||||
}
|
||||
.onChange(of: watcher.latestEvent?.id) { _ in
|
||||
.onChange(of: watcher.latestEvent?.id) {
|
||||
if let latestEvent = watcher.latestEvent {
|
||||
viewModel.handleEvent(event: latestEvent, currentAccount: account)
|
||||
}
|
||||
}
|
||||
.onChange(of: timeline) { newTimeline in
|
||||
switch newTimeline {
|
||||
.onChange(of: timeline) { oldValue, newValue in
|
||||
switch newValue {
|
||||
case let .remoteLocal(server, _):
|
||||
viewModel.client = Client(server: server)
|
||||
default:
|
||||
viewModel.client = client
|
||||
}
|
||||
viewModel.timeline = newTimeline
|
||||
viewModel.timeline = newValue
|
||||
}
|
||||
.onChange(of: viewModel.timeline, perform: { newValue in
|
||||
.onChange(of: viewModel.timeline) { oldValue, newValue in
|
||||
timeline = newValue
|
||||
})
|
||||
.onChange(of: scenePhase, perform: { scenePhase in
|
||||
switch scenePhase {
|
||||
}
|
||||
.onChange(of: scenePhase) { oldValue, newValue in
|
||||
switch newValue {
|
||||
case .active:
|
||||
if wasBackgrounded {
|
||||
wasBackgrounded = false
|
||||
|
@ -175,7 +175,7 @@ public struct TimelineView: View {
|
|||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
|
Loading…
Reference in a new issue