Move to observables

This commit is contained in:
Marcin Czachurski 2023-10-19 13:24:02 +02:00
parent 27cba57c38
commit a8acc93c3e
75 changed files with 284 additions and 238 deletions

View File

@ -7,7 +7,7 @@
import Foundation import Foundation
import PixelfedKit import PixelfedKit
public class Client: ObservableObject { @Observable public class Client {
public static let shared = Client() public static let shared = Client()
private init() { } private init() { }

View File

@ -6,7 +6,7 @@
import Foundation import Foundation
public class AccountModel: ObservableObject, Identifiable { @Observable public class AccountModel: Identifiable {
public let id: String public let id: String
public let accessToken: String? public let accessToken: String?
public let refreshToken: String? public let refreshToken: String?
@ -28,7 +28,7 @@ public class AccountModel: ObservableObject, Identifiable {
public let username: String public let username: String
public let lastSeenStatusId: String? public let lastSeenStatusId: String?
@Published public var avatarData: Data? public var avatarData: Data?
public init(id: String, public init(id: String,
accessToken: String?, accessToken: String?,

View File

@ -7,7 +7,7 @@
import Foundation import Foundation
import PixelfedKit import PixelfedKit
public class AttachmentModel: ObservableObject, Identifiable { @Observable public class AttachmentModel: Identifiable {
public let id: String public let id: String
public let type: MediaAttachment.MediaAttachmentType public let type: MediaAttachment.MediaAttachmentType
public let url: URL public let url: URL
@ -21,11 +21,11 @@ public class AttachmentModel: ObservableObject, Identifiable {
public let metaImageWidth: Int32? public let metaImageWidth: Int32?
public let metaImageHeight: Int32? public let metaImageHeight: Int32?
@Published public var exifCamera: String? public var exifCamera: String?
@Published public var exifCreatedDate: String? public var exifCreatedDate: String?
@Published public var exifExposure: String? public var exifExposure: String?
@Published public var exifLens: String? public var exifLens: String?
@Published public var data: Data? public var data: Data?
public init(id: String, public init(id: String,
type: MediaAttachment.MediaAttachmentType, type: MediaAttachment.MediaAttachmentType,

View File

@ -7,7 +7,7 @@
import Foundation import Foundation
import PixelfedKit import PixelfedKit
public class StatusModel: ObservableObject { @Observable public class StatusModel {
public let id: EntityId public let id: EntityId
public let rebloggedStatusId: EntityId? public let rebloggedStatusId: EntityId?
public let content: Html public let content: Html
@ -38,8 +38,8 @@ public class StatusModel: ObservableObject {
public let reblogStatus: Status? public let reblogStatus: Status?
@Published public var favourited: Bool public var favourited: Bool
@Published public var mediaAttachments: [AttachmentModel] public var mediaAttachments: [AttachmentModel]
public init(status: Status) { public init(status: Status) {
self.id = status.id self.id = status.id

View File

@ -9,7 +9,7 @@ import SwiftUI
import PixelfedKit import PixelfedKit
import ClientKit import ClientKit
public class ApplicationState: ObservableObject { @Observable public class ApplicationState {
public static let shared = ApplicationState() public static let shared = ApplicationState()
private init() { } private init() { }
@ -24,94 +24,94 @@ public class ApplicationState: ObservableObject {
private static let defaults = Defaults() private static let defaults = Defaults()
/// Actual signed in account. /// Actual signed in account.
@Published public private(set) var account: AccountModel? public private(set) var account: AccountModel?
/// The maximum number of allowed characters per status. /// The maximum number of allowed characters per status.
@Published public private(set) var statusMaxCharacters = defaults.statusMaxCharacters public private(set) var statusMaxCharacters = defaults.statusMaxCharacters
/// The maximum number of media attachments that can be added to a status. /// The maximum number of media attachments that can be added to a status.
@Published public private(set) var statusMaxMediaAttachments = defaults.statusMaxMediaAttachments public private(set) var statusMaxMediaAttachments = defaults.statusMaxMediaAttachments
/// Each URL in a status will be assumed to be exactly this many characters. /// Each URL in a status will be assumed to be exactly this many characters.
@Published public private(set) var statusCharactersReservedPerUrl = defaults.statusCharactersReservedPerUrl public private(set) var statusCharactersReservedPerUrl = defaults.statusCharactersReservedPerUrl
/// Last status seen by the user. /// Last status seen by the user.
@Published public var lastSeenStatusId: String? public var lastSeenStatusId: String?
/// Amount of new statuses which are not displayed yet to the user. /// Amount of new statuses which are not displayed yet to the user.
@Published public var amountOfNewStatuses = 0 public var amountOfNewStatuses = 0
/// Model for newly created comment. /// Model for newly created comment.
@Published public var newComment: CommentModel? public var newComment: CommentModel?
/// Active icon name. /// Active icon name.
@Published public var activeIcon = "Default" public var activeIcon = "Default"
/// Tint color in whole application. /// Tint color in whole application.
@Published public var tintColor = TintColor.accentColor2 public var tintColor = TintColor.accentColor2
/// Application theme. /// Application theme.
@Published public var theme = Theme.system public var theme = Theme.system
/// Avatar shape. /// Avatar shape.
@Published public var avatarShape = AvatarShape.circle public var avatarShape = AvatarShape.circle
/// Status id for showed interaction row. /// Status id for showed interaction row.
@Published public var showInteractionStatusId = "" public var showInteractionStatusId = ""
/// Should we fire haptic when user change tabs. /// Should we fire haptic when user change tabs.
@Published public var hapticTabSelectionEnabled = true public var hapticTabSelectionEnabled = true
/// Should we fire haptic when user refresh list. /// Should we fire haptic when user refresh list.
@Published public var hapticRefreshEnabled = true public var hapticRefreshEnabled = true
/// Should we fire haptic when user tap button. /// Should we fire haptic when user tap button.
@Published public var hapticButtonPressEnabled = true public var hapticButtonPressEnabled = true
/// Should we fire haptic when animation is finished. /// Should we fire haptic when animation is finished.
@Published public var hapticAnimationEnabled = true public var hapticAnimationEnabled = true
/// Should we fire haptic when notification occures. /// Should we fire haptic when notification occures.
@Published public var hapticNotificationEnabled = true public var hapticNotificationEnabled = true
/// Should sensitive photos without mask. /// Should sensitive photos without mask.
@Published public var showSensitive = false public var showSensitive = false
/// Should photo description for visually impaired be displayed. /// Should photo description for visually impaired be displayed.
@Published public var showPhotoDescription = false public var showPhotoDescription = false
/// Status which should be shown from URL. /// Status which should be shown from URL.
@Published public var showStatusId: String? public var showStatusId: String?
/// Account which should be shown from URL. /// Account which should be shown from URL.
@Published public var showAccountId: String? public var showAccountId: String?
/// Updated user profile. /// Updated user profile.
@Published public var updatedProfile: Account? public var updatedProfile: Account?
/// Information which menu should be shown (top or bottom). /// Information which menu should be shown (top or bottom).
@Published public var menuPosition = MenuPosition.top public var menuPosition = MenuPosition.top
/// Should avatars be visible on timelines. /// Should avatars be visible on timelines.
@Published public var showAvatarsOnTimeline = false public var showAvatarsOnTimeline = false
/// Should favourites be visible on timelines. /// Should favourites be visible on timelines.
@Published public var showFavouritesOnTimeline = false public var showFavouritesOnTimeline = false
/// Should ALT icon be visible on timelines. /// Should ALT icon be visible on timelines.
@Published public var showAltIconOnTimeline = false public var showAltIconOnTimeline = false
/// Show warning about missing ALT texts on compose screen. /// Show warning about missing ALT texts on compose screen.
@Published public var warnAboutMissingAlt = true public var warnAboutMissingAlt = true
/// Show grid of photos on user profile. /// Show grid of photos on user profile.
@Published public var showGridOnUserProfile = false public var showGridOnUserProfile = false
/// Show reboosted statuses on home timeline. /// Show reboosted statuses on home timeline.
@Published public var showReboostedStatuses = false public var showReboostedStatuses = false
/// Hide statuses without ALT text. /// Hide statuses without ALT text.
@Published public var hideStatusesWithoutAlt = false public var hideStatusesWithoutAlt = false
public func changeApplicationState(accountModel: AccountModel, instance: Instance?, lastSeenStatusId: String?) { public func changeApplicationState(accountModel: AccountModel, instance: Instance?, lastSeenStatusId: String?) {
self.account = accountModel self.account = accountModel

View File

@ -28,7 +28,6 @@ public class HapticService {
impactGenerator.prepare() impactGenerator.prepare()
} }
@MainActor
public func fireHaptic(of type: HapticType) { public func fireHaptic(of type: HapticType) {
guard supportsHaptics else { return } guard supportsHaptics else { return }

View File

@ -8,8 +8,8 @@ import Foundation
import SwiftUI import SwiftUI
import WidgetsKit import WidgetsKit
@MainActor
extension View { extension View {
func withAppRouteur() -> some View { func withAppRouteur() -> some View {
self.navigationDestination(for: RouteurDestinations.self) { destination in self.navigationDestination(for: RouteurDestinations.self) { destination in
switch destination { switch destination {

View File

@ -10,14 +10,13 @@ import ServicesKit
import OSLog import OSLog
import EnvironmentKit import EnvironmentKit
@MainActor @Observable final class TipsStore {
final class TipsStore: ObservableObject {
/// Products are registered in AppStore connect (and for development in InAppPurchaseStoreKitConfiguration.storekit file). /// Products are registered in AppStore connect (and for development in InAppPurchaseStoreKitConfiguration.storekit file).
@Published private(set) var items = [Product]() private(set) var items = [Product]()
/// Status of the purchase. /// Status of the purchase.
@Published private(set) var status: ActionStatus? { private(set) var status: ActionStatus? {
didSet { didSet {
switch status { switch status {
case .failed: case .failed:
@ -29,7 +28,7 @@ final class TipsStore: ObservableObject {
} }
/// True when error during purchase occures. /// True when error during purchase occures.
@Published var hasError = false var hasError = false
/// Error during purchase. /// Error during purchase.
var error: PurchaseError? { var error: PurchaseError? {

View File

@ -7,11 +7,11 @@
import Foundation import Foundation
import SwiftUI import SwiftUI
class NavigationMenuItemDetails: ObservableObject, Identifiable { @Observable class NavigationMenuItemDetails: Identifiable {
@Published var title: LocalizedStringKey var title: LocalizedStringKey
@Published var image: String var image: String
@Published var viewMode: MainView.ViewMode { var viewMode: MainView.ViewMode {
didSet { didSet {
self.title = viewMode.title self.title = viewMode.title
self.image = viewMode.image self.image = viewMode.image

View File

@ -6,13 +6,13 @@
import Foundation import Foundation
public class PhotoUrl: ObservableObject, Identifiable { @Observable public class PhotoUrl: Identifiable {
public var id: String public var id: String
@Published public var statusId: String? public var statusId: String?
@Published public var url: URL? public var url: URL?
@Published public var blurhash: String? public var blurhash: String?
@Published public var sensitive = false public var sensitive = false
init(id: String) { init(id: String) {
self.id = id self.id = id

View File

@ -8,7 +8,7 @@ import SwiftUI
import Foundation import Foundation
import PixelfedKit import PixelfedKit
public class RelationshipModel: ObservableObject { @Observable public class RelationshipModel {
enum RelationshipAction { enum RelationshipAction {
case follow case follow
case unfollow case unfollow
@ -17,46 +17,46 @@ public class RelationshipModel: ObservableObject {
} }
/// The account ID. /// The account ID.
@Published public var id: EntityId public var id: EntityId
/// Are you followed by this user? /// Are you followed by this user?
@Published public var followedBy: Bool public var followedBy: Bool
/// Is this user blocking you? /// Is this user blocking you?
@Published public var blockedBy: Bool public var blockedBy: Bool
/// Are you muting notifications from this user? /// Are you muting notifications from this user?
@Published public var mutingNotifications: Bool public var mutingNotifications: Bool
/// Do you have a pending follow request for this user? /// Do you have a pending follow request for this user?
@Published public var requested: Bool public var requested: Bool
/// Are you receiving this users boosts in your home timeline? /// Are you receiving this users boosts in your home timeline?
@Published public var showingReblogs: Bool public var showingReblogs: Bool
/// Have you enabled notifications for this user? /// Have you enabled notifications for this user?
@Published public var notifying: Bool public var notifying: Bool
/// Are you blocking this users domain? /// Are you blocking this users domain?
@Published public var domainBlocking: Bool public var domainBlocking: Bool
/// Are you featuring this user on your profile? /// Are you featuring this user on your profile?
@Published public var endorsed: Bool public var endorsed: Bool
/// Which languages are you following from this user? Array of String (ISO 639-1 language two-letter code). /// Which languages are you following from this user? Array of String (ISO 639-1 language two-letter code).
@Published public var languages: [String]? public var languages: [String]?
/// This users profile bio. /// This users profile bio.
@Published public var note: String? public var note: String?
/// Are you following this user? /// Are you following this user?
@Published public var following: Bool public var following: Bool
/// Are you blocking this user? /// Are you blocking this user?
@Published public var blocking: Bool public var blocking: Bool
/// Are you muting this user? /// Are you muting this user?
@Published public var muting: Bool public var muting: Bool
public init() { public init() {
self.id = "" self.id = ""

View File

@ -90,7 +90,6 @@ public class HomeTimelineService {
return lastSeenStatusId return lastSeenStatusId
} }
@MainActor
public func update(attachment: AttachmentData, withData imageData: Data, imageWidth: Double, imageHeight: Double) { public func update(attachment: AttachmentData, withData imageData: Data, imageWidth: Double, imageHeight: Double) {
attachment.data = imageData attachment.data = imageData
attachment.metaImageWidth = Int32(imageWidth) attachment.metaImageWidth = Int32(imageWidth)

View File

@ -69,13 +69,13 @@ enum AlertDestinations: Identifiable {
} }
@MainActor @MainActor
class RouterPath: ObservableObject { @Observable class RouterPath {
public var urlHandler: ((URL) -> OpenURLAction.Result)? public var urlHandler: ((URL) -> OpenURLAction.Result)?
@Published public var path: [RouteurDestinations] = [] public var path: [RouteurDestinations] = []
@Published public var presentedSheet: SheetDestinations? public var presentedSheet: SheetDestinations?
@Published public var presentedOverlay: OverlayDestinations? public var presentedOverlay: OverlayDestinations?
@Published public var presentedAlert: AlertDestinations? public var presentedAlert: AlertDestinations?
public init() {} public init() {}

View File

@ -17,10 +17,10 @@ struct VernissageApp: App {
let coreDataHandler = CoreDataHandler.shared let coreDataHandler = CoreDataHandler.shared
@StateObject var applicationState = ApplicationState.shared @State var applicationState = ApplicationState.shared
@StateObject var client = Client.shared @State var client = Client.shared
@StateObject var routerPath = RouterPath() @State var routerPath = RouterPath()
@StateObject var tipsStore = TipsStore() @State var tipsStore = TipsStore()
@State var applicationViewMode: ApplicationViewMode = .loading @State var applicationViewMode: ApplicationViewMode = .loading
@State var tintColor = ApplicationState.shared.tintColor.color() @State var tintColor = ApplicationState.shared.tintColor.color()
@ -30,7 +30,7 @@ struct VernissageApp: App {
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
NavigationStack(path: $routerPath.path) { NavigationStack {
switch applicationViewMode { switch applicationViewMode {
case .loading: case .loading:
LoadingView() LoadingView()
@ -51,16 +51,15 @@ struct VernissageApp: App {
} }
} }
.environment(\.managedObjectContext, coreDataHandler.container.viewContext) .environment(\.managedObjectContext, coreDataHandler.container.viewContext)
.environmentObject(applicationState) .environment(applicationState)
.environmentObject(client) .environment(client)
.environmentObject(routerPath) .environment(routerPath)
.environmentObject(tipsStore) .environment(tipsStore)
.tint(self.tintColor) .tint(self.tintColor)
.preferredColorScheme(self.theme) .preferredColorScheme(self.theme)
.task { .task {
await self.onApplicationStart() await self.onApplicationStart()
} }
.navigationViewStyle(.stack)
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in .onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 5) { DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
Task { Task {

View File

@ -34,8 +34,8 @@ public extension View {
} }
private struct ImageContextMenu: ViewModifier { private struct ImageContextMenu: ViewModifier {
@EnvironmentObject var client: Client @Environment(Client.self) var client
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
private let id: String private let id: String
private let url: URL? private let url: URL?

View File

@ -9,6 +9,7 @@ import SwiftUI
import EnvironmentKit import EnvironmentKit
import ServicesKit import ServicesKit
@MainActor
extension View { extension View {
func navigationMenuButtons(menuPosition: Binding<MenuPosition>, func navigationMenuButtons(menuPosition: Binding<MenuPosition>,
onViewModeIconTap: @escaping (MainView.ViewMode) -> Void) -> some View { onViewModeIconTap: @escaping (MainView.ViewMode) -> Void) -> some View {
@ -16,8 +17,9 @@ extension View {
} }
} }
@MainActor
private struct NavigationMenuButtons: ViewModifier { private struct NavigationMenuButtons: ViewModifier {
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
private let onViewModeIconTap: (MainView.ViewMode) -> Void private let onViewModeIconTap: (MainView.ViewMode) -> Void
private let imageFontSize = 20.0 private let imageFontSize = 20.0

View File

@ -12,6 +12,7 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct AccountsPhotoView: View { struct AccountsPhotoView: View {
public enum ListType: Hashable { public enum ListType: Hashable {
case trending case trending
@ -27,9 +28,9 @@ struct AccountsPhotoView: View {
} }
} }
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(Client.self) var client
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@State public var listType: ListType @State public var listType: ListType

View File

@ -12,6 +12,7 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct AccountsView: View { struct AccountsView: View {
public enum ListType: Hashable { public enum ListType: Hashable {
case followers(entityId: String) case followers(entityId: String)
@ -42,8 +43,8 @@ struct AccountsView: View {
} }
} }
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(Client.self) var client
@State var listType: ListType @State var listType: ListType

View File

@ -8,6 +8,7 @@ import Foundation
import UIKit import UIKit
import SwiftUI import SwiftUI
@MainActor
struct ActivityView: UIViewControllerRepresentable { struct ActivityView: UIViewControllerRepresentable {
let image: UIImage let image: UIImage

View File

@ -13,9 +13,10 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct ComposeView: View { struct ComposeView: View {
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@EnvironmentObject var client: Client @Environment(Client.self) var client
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@ -26,6 +27,8 @@ struct ComposeView: View {
} }
var body: some View { var body: some View {
@Bindable var routerPath = routerPath
NavigationView { NavigationView {
BaseComposeView(statusViewModel: self.statusViewModel) { BaseComposeView(statusViewModel: self.statusViewModel) {
dismiss() dismiss()

View File

@ -13,9 +13,10 @@ import ServicesKit
import WidgetsKit import WidgetsKit
import EnvironmentKit import EnvironmentKit
@MainActor
struct EditProfileView: View { struct EditProfileView: View {
@EnvironmentObject private var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject private var client: Client @Environment(Client.self) var client
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@State private var account: Account? @State private var account: Account?

View File

@ -12,10 +12,11 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct FollowRequestsView: View { struct FollowRequestsView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@EnvironmentObject var client: Client @Environment(Client.self) var client
@State private var accounts: [Account] = [] @State private var accounts: [Account] = []
@State private var downloadedPage = 1 @State private var downloadedPage = 1

View File

@ -12,6 +12,7 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct HashtagsView: View { struct HashtagsView: View {
public enum ListType: Hashable { public enum ListType: Hashable {
case trending case trending
@ -30,9 +31,9 @@ struct HashtagsView: View {
} }
} }
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(Client.self) var client
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@State public var listType: ListType @State public var listType: ListType

View File

@ -11,11 +11,12 @@ import WidgetsKit
import OSLog import OSLog
import Semaphore import Semaphore
@MainActor
struct HomeFeedView: View { struct HomeFeedView: View {
@Environment(\.managedObjectContext) private var viewContext @Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@State private var allItemsLoaded = false @State private var allItemsLoaded = false
@State private var state: ViewState = .loading @State private var state: ViewState = .loading

View File

@ -12,10 +12,11 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct InstanceView: View { struct InstanceView: View {
@EnvironmentObject private var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject private var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@EnvironmentObject private var client: Client @Environment(Client.self) var client
@State private var state: ViewState = .loading @State private var state: ViewState = .loading
@State private var instance: Instance? @State private var instance: Instance?

View File

@ -6,6 +6,7 @@
import SwiftUI import SwiftUI
@MainActor
struct LoadingView: View { struct LoadingView: View {
var body: some View { var body: some View {
VStack(alignment: .center) { VStack(alignment: .center) {

View File

@ -12,13 +12,14 @@ import ClientKit
import ServicesKit import ServicesKit
import EnvironmentKit import EnvironmentKit
@MainActor
struct MainView: View { struct MainView: View {
@Environment(\.managedObjectContext) private var viewContext @Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(Client.self) var client
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@EnvironmentObject var tipsStore: TipsStore @Environment(TipsStore.self) var tipsStore
@State private var navBarTitle: LocalizedStringKey = ViewMode.home.title @State private var navBarTitle: LocalizedStringKey = ViewMode.home.title
@State private var viewMode: ViewMode = .home { @State private var viewMode: ViewMode = .home {
@ -88,28 +89,33 @@ struct MainView: View {
} }
var body: some View { var body: some View {
self.getMainView() @Bindable var applicationState = applicationState
.navigationMenuButtons(menuPosition: $applicationState.menuPosition) { viewMode in @Bindable var routerPath = routerPath
self.switchView(to: viewMode)
}
.navigationTitle(navBarTitle)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
self.getLeadingToolbar()
if self.applicationState.menuPosition == .top { NavigationStack(path: $routerPath.path) {
self.getPrincipalToolbar() self.getMainView()
self.getTrailingToolbar() .navigationMenuButtons(menuPosition: $applicationState.menuPosition) { viewMode in
self.switchView(to: viewMode)
} }
} .navigationTitle(navBarTitle)
.onChange(of: tipsStore.status) { oldStatus, newStatus in .navigationBarTitleDisplayMode(.inline)
if newStatus == .successful { .toolbar {
withAnimation(.spring()) { self.getLeadingToolbar()
self.routerPath.presentedOverlay = .successPayment
self.tipsStore.reset() if self.applicationState.menuPosition == .top {
self.getPrincipalToolbar()
self.getTrailingToolbar()
} }
} }
} .onChange(of: tipsStore.status) { oldStatus, newStatus in
if newStatus == .successful {
withAnimation(.spring()) {
self.routerPath.presentedOverlay = .successPayment
self.tipsStore.reset()
}
}
}
}
} }
@ViewBuilder @ViewBuilder

View File

@ -11,9 +11,10 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct NotificationsView: View { struct NotificationsView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(Client.self) var client
@State var accountId: String @State var accountId: String
@State private var notifications: [PixelfedKit.Notification] = [] @State private var notifications: [PixelfedKit.Notification] = []

View File

@ -13,9 +13,9 @@ import EnvironmentKit
import WidgetsKit import WidgetsKit
struct NotificationRowView: View { struct NotificationRowView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@EnvironmentObject var client: Client @Environment(Client.self) var client
@State private var image: SwiftUI.Image? @State private var image: SwiftUI.Image?

View File

@ -12,6 +12,7 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct PaginableStatusesView: View { struct PaginableStatusesView: View {
public enum ListType: Hashable { public enum ListType: Hashable {
case favourites case favourites
@ -27,9 +28,9 @@ struct PaginableStatusesView: View {
} }
} }
@EnvironmentObject private var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject private var client: Client @Environment(Client.self) var client
@EnvironmentObject private var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@State public var listType: ListType @State public var listType: ListType

View File

@ -10,8 +10,9 @@ import ClientKit
import ServicesKit import ServicesKit
import WidgetsKit import WidgetsKit
@MainActor
struct ReportView: View { struct ReportView: View {
@EnvironmentObject private var client: Client @Environment(Client.self) var client
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@State private var publishDisabled = false @State private var publishDisabled = false

View File

@ -6,8 +6,9 @@
import SwiftUI import SwiftUI
@MainActor
struct SearchView: View { struct SearchView: View {
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@State private var query = String.empty() @State private var query = String.empty()

View File

@ -7,10 +7,11 @@
import SwiftUI import SwiftUI
import EnvironmentKit import EnvironmentKit
@MainActor
struct SettingsView: View { struct SettingsView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@EnvironmentObject var tipsStore: TipsStore @Environment(TipsStore.self) var tipsStore
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@ -20,6 +21,9 @@ struct SettingsView: View {
@State private var appBundleVersion: String? @State private var appBundleVersion: String?
var body: some View { var body: some View {
@Bindable var routerPath = routerPath
@Bindable var tipsStore = tipsStore
NavigationStack { NavigationStack {
NavigationView { NavigationView {
List { List {

View File

@ -8,7 +8,7 @@ import SwiftUI
import EnvironmentKit import EnvironmentKit
struct AccentsSectionView: View { struct AccentsSectionView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
private let accentColors1: [TintColor] = [.accentColor1, .accentColor2, .accentColor3, .accentColor4, .accentColor5] private let accentColors1: [TintColor] = [.accentColor1, .accentColor2, .accentColor3, .accentColor4, .accentColor5]
private let accentColors2: [TintColor] = [.accentColor6, .accentColor7, .accentColor8, .accentColor9, .accentColor10] private let accentColors2: [TintColor] = [.accentColor6, .accentColor7, .accentColor8, .accentColor9, .accentColor10]

View File

@ -10,8 +10,8 @@ import EnvironmentKit
import WidgetsKit import WidgetsKit
struct AccountsSectionView: View { struct AccountsSectionView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(Client.self) var client
@State private var accounts: [AccountModel] = [] @State private var accounts: [AccountModel] = []
@State private var dbAccounts: [AccountData] = [] @State private var dbAccounts: [AccountData] = []

View File

@ -8,7 +8,7 @@ import SwiftUI
import EnvironmentKit import EnvironmentKit
struct AvatarShapesSectionView: View { struct AvatarShapesSectionView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
var body: some View { var body: some View {
Section("settings.title.avatar") { Section("settings.title.avatar") {

View File

@ -8,7 +8,7 @@ import SwiftUI
import EnvironmentKit import EnvironmentKit
struct GeneralSectionView: View { struct GeneralSectionView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
private let customIconNames = ["Default", private let customIconNames = ["Default",
"Blue", "Blue",
@ -41,8 +41,9 @@ struct GeneralSectionView: View {
] ]
var body: some View { var body: some View {
@Bindable var applicationState = applicationState
Section("settings.title.general") { Section("settings.title.general") {
// Application icon. // Application icon.
Picker(selection: $applicationState.activeIcon) { Picker(selection: $applicationState.activeIcon) {
ForEach(self.customIconNames, id: \.self) { icon in ForEach(self.customIconNames, id: \.self) { icon in

View File

@ -8,7 +8,7 @@ import SwiftUI
import EnvironmentKit import EnvironmentKit
struct HapticsSectionView: View { struct HapticsSectionView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@State var hapticTabSelectionEnabled = true @State var hapticTabSelectionEnabled = true

View File

@ -8,10 +8,12 @@ import SwiftUI
import EnvironmentKit import EnvironmentKit
struct MediaSettingsView: View { struct MediaSettingsView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
var body: some View { var body: some View {
@Bindable var applicationState = applicationState
Section("settings.title.mediaSettings") { Section("settings.title.mediaSettings") {
Toggle(isOn: $applicationState.showSensitive) { Toggle(isOn: $applicationState.showSensitive) {

View File

@ -9,7 +9,7 @@ import StoreKit
import ServicesKit import ServicesKit
struct SupportView: View { struct SupportView: View {
@EnvironmentObject var tipsStore: TipsStore @Environment(TipsStore.self) var tipsStore
var body: some View { var body: some View {
Section("settings.title.support") { Section("settings.title.support") {

View File

@ -8,7 +8,7 @@ import SwiftUI
import ServicesKit import ServicesKit
struct ThanksView: View { struct ThanksView: View {
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
var body: some View { var body: some View {
VStack { VStack {

View File

@ -12,12 +12,14 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct SignInView: View { struct SignInView: View {
@Environment(\.managedObjectContext) private var viewContext @Environment(\.managedObjectContext) private var viewContext
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(RouterPath.self) var routerPath
@Environment(Client.self) var client
@State private var serverAddress = String.empty() @State private var serverAddress = String.empty()
@State private var instructionsUrlString: String? @State private var instructionsUrlString: String?
@ -39,10 +41,10 @@ struct SignInView: View {
.keyboardType(.URL) .keyboardType(.URL)
.disableAutocorrection(true) .disableAutocorrection(true)
.clearButton(text: $serverAddress) .clearButton(text: $serverAddress)
Button(NSLocalizedString("signin.title.signIn", comment: "Sign in")) { Button(NSLocalizedString("signin.title.signIn", comment: "Sign in")) {
HapticService.shared.fireHaptic(of: .buttonPress) HapticService.shared.fireHaptic(of: .buttonPress)
let baseAddress = self.getServerAddress(uri: self.serverAddress) let baseAddress = self.getServerAddress(uri: self.serverAddress)
self.signIn(baseAddress: baseAddress) self.signIn(baseAddress: baseAddress)
} }
@ -51,7 +53,7 @@ struct SignInView: View {
} }
} }
.buttonStyle(PlainButtonStyle()) .buttonStyle(PlainButtonStyle())
} header: { } header: {
Text("signin.title.enterServerAddress", comment: "Enter server address") Text("signin.title.enterServerAddress", comment: "Enter server address")
} footer: { } footer: {
@ -64,7 +66,7 @@ struct SignInView: View {
} }
} }
} }
Section("signin.title.chooseServer") { Section("signin.title.chooseServer") {
if self.instances.isEmpty { if self.instances.isEmpty {
HStack { HStack {
@ -73,7 +75,7 @@ struct SignInView: View {
Spacer() Spacer()
} }
} }
ForEach(self.instances.filter { self.serverAddress.isEmpty || $0.uri.contains(self.serverAddress) }, id: \.uri) { instance in ForEach(self.instances.filter { self.serverAddress.isEmpty || $0.uri.contains(self.serverAddress) }, id: \.uri) { instance in
InstanceRowView(instance: instance) { uri in InstanceRowView(instance: instance) { uri in
let baseAddress = self.getServerAddress(uri: uri) let baseAddress = self.getServerAddress(uri: uri)

View File

@ -11,7 +11,7 @@ import ServicesKit
import WidgetsKit import WidgetsKit
struct InstanceRowView: View { struct InstanceRowView: View {
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
private let instance: Instance private let instance: Instance
private let action: (String) -> Void private let action: (String) -> Void

View File

@ -12,6 +12,7 @@ import ServicesKit
import WidgetsKit import WidgetsKit
import EnvironmentKit import EnvironmentKit
@MainActor
struct StatusView: View { struct StatusView: View {
struct TappedAttachment: Identifiable { struct TappedAttachment: Identifiable {
public let id: String public let id: String
@ -19,9 +20,9 @@ struct StatusView: View {
public let imagePosition: Double public let imagePosition: Double
} }
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(Client.self) var client
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss

View File

@ -11,8 +11,8 @@ import EnvironmentKit
import WidgetsKit import WidgetsKit
struct CommentBodyView: View { struct CommentBodyView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@State var statusViewModel: StatusModel @State var statusViewModel: StatusModel

View File

@ -13,8 +13,8 @@ import WidgetsKit
struct CommentsSectionView: View { struct CommentsSectionView: View {
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(Client.self) var client
@State public var statusId: String @State public var statusId: String
@State private var commentViewModels: [CommentModel]? @State private var commentViewModels: [CommentModel]?

View File

@ -12,6 +12,7 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct StatusesView: View { struct StatusesView: View {
public enum ListType: Hashable { public enum ListType: Hashable {
case home case home
@ -39,9 +40,9 @@ struct StatusesView: View {
} }
} }
@EnvironmentObject private var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject private var client: Client @Environment(Client.self) var client
@EnvironmentObject private var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss

View File

@ -6,6 +6,7 @@
import SwiftUI import SwiftUI
@MainActor
struct ThirdPartyView: View { struct ThirdPartyView: View {
var body: some View { var body: some View {
List { List {

View File

@ -11,9 +11,10 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct TrendStatusesView: View { struct TrendStatusesView: View {
@EnvironmentObject private var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject private var client: Client @Environment(Client.self) var client
@State public var accountId: String @State public var accountId: String

View File

@ -12,12 +12,12 @@ import WidgetsKit
import EnvironmentKit import EnvironmentKit
struct UserProfileHeaderView: View { struct UserProfileHeaderView: View {
@EnvironmentObject private var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject private var client: Client @Environment(Client.self) var client
@EnvironmentObject private var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@State var account: Account @State var account: Account
@ObservedObject var relationship = RelationshipModel() @State var relationship = RelationshipModel()
@Binding var boostsDisabled: Bool @Binding var boostsDisabled: Bool
var body: some View { var body: some View {

View File

@ -13,8 +13,8 @@ import EnvironmentKit
import WidgetsKit import WidgetsKit
struct UserProfileStatusesView: View { struct UserProfileStatusesView: View {
@EnvironmentObject private var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject private var client: Client @Environment(Client.self) var client
@State public var accountId: String @State public var accountId: String
@ -40,6 +40,8 @@ struct UserProfileStatusesView: View {
} }
var body: some View { var body: some View {
@Bindable var applicationState = applicationState
if firstLoadFinished == true { if firstLoadFinished == true {
if self.imageColumns > 1 { if self.imageColumns > 1 {
WaterfallGrid($statusViewModels, refreshId: Binding.constant(""), columns: $imageColumns, hideLoadMore: $allItemsLoaded) { item in WaterfallGrid($statusViewModels, refreshId: Binding.constant(""), columns: $imageColumns, hideLoadMore: $allItemsLoaded) { item in

View File

@ -11,10 +11,11 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct UserProfileView: View { struct UserProfileView: View {
@EnvironmentObject private var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject private var client: Client @Environment(Client.self) var client
@EnvironmentObject private var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@ -22,7 +23,7 @@ struct UserProfileView: View {
@State public var accountDisplayName: String? @State public var accountDisplayName: String?
@State public var accountUserName: String @State public var accountUserName: String
@StateObject private var relationship = RelationshipModel() @State private var relationship = RelationshipModel()
@State private var account: Account? @State private var account: Account?
@State private var state: ViewState = .loading @State private var state: ViewState = .loading
@State private var viewId = UUID().uuidString @State private var viewId = UUID().uuidString

View File

@ -10,7 +10,7 @@ import ServicesKit
import WidgetsKit import WidgetsKit
struct ImageCarouselPicture: View { struct ImageCarouselPicture: View {
@ObservedObject public var attachment: AttachmentModel public var attachment: AttachmentModel
@State private var blurredImageHeight: Double @State private var blurredImageHeight: Double
@State private var blurredImageWidth: Double @State private var blurredImageWidth: Double

View File

@ -10,14 +10,14 @@ import EnvironmentKit
import WidgetsKit import WidgetsKit
struct ImageGrid: View { struct ImageGrid: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@StateObject var photoUrl: PhotoUrl var photoUrl: PhotoUrl
@Binding var maxHeight: Double @Binding var maxHeight: Double
init(photoUrl: PhotoUrl, maxHeight: Binding<Double>) { init(photoUrl: PhotoUrl, maxHeight: Binding<Double>) {
self._photoUrl = StateObject(wrappedValue: photoUrl) self.photoUrl = photoUrl
self._maxHeight = maxHeight self._maxHeight = maxHeight
} }

View File

@ -11,10 +11,11 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct ImageRowItem: View { struct ImageRowItem: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(Client.self) var client
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
private let status: StatusData private let status: StatusData
private let attachmentData: AttachmentData private let attachmentData: AttachmentData
@ -267,7 +268,7 @@ struct ImageRowItem: View {
self.cancelled = false self.cancelled = false
} }
private func navigateToStatus() { private func navigateToStatus() {
self.routerPath.navigate(to: .status( self.routerPath.navigate(to: .status(
id: status.id, id: status.id,
blurhash: status.attachments().first?.blurhash, blurhash: status.attachments().first?.blurhash,

View File

@ -13,10 +13,11 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
@MainActor
struct ImageRowItemAsync: View { struct ImageRowItemAsync: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(Client.self) var client
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
private var statusViewModel: StatusModel private var statusViewModel: StatusModel
private var attachment: AttachmentModel private var attachment: AttachmentModel

View File

@ -10,14 +10,15 @@ import ClientKit
import NukeUI import NukeUI
import ServicesKit import ServicesKit
@MainActor
struct ImagesGrid: View { struct ImagesGrid: View {
public enum GridType: Hashable { public enum GridType: Hashable {
case account(accountId: String, accountDisplayName: String?, accountUserName: String) case account(accountId: String, accountDisplayName: String?, accountUserName: String)
case hashtag(name: String) case hashtag(name: String)
} }
@EnvironmentObject var client: Client @Environment(Client.self) var client
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
private let maxImages = 5 private let maxImages = 5

View File

@ -15,9 +15,9 @@ import WidgetsKit
struct InteractionRow: View { struct InteractionRow: View {
typealias DeleteAction = () -> Void typealias DeleteAction = () -> Void
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(Client.self) var client
@EnvironmentObject var routerPath: RouterPath @Environment(RouterPath.self) var routerPath
@State var statusModel: StatusModel @State var statusModel: StatusModel

View File

@ -14,7 +14,7 @@ import WidgetsKit
import ServicesKit import ServicesKit
struct ComposeView: View { struct ComposeView: View {
@EnvironmentObject var client: Client @Environment(Client.self) var client
private let attachments: [NSItemProvider] private let attachments: [NSItemProvider]

View File

@ -45,8 +45,8 @@ class ShareViewController: UIViewController {
// Create view. // Create view.
let view = ComposeView(attachments: attachments) let view = ComposeView(attachments: attachments)
.environmentObject(applicationState) .environment(applicationState)
.environmentObject(client) .environment(client)
.tint(applicationState.tintColor.color()) .tint(applicationState.tintColor.color())
// Add view to current UIViewController. // Add view to current UIViewController.

View File

@ -15,6 +15,7 @@ public extension View {
} }
} }
@MainActor
func hideKeyboard() { func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
} }

View File

@ -10,32 +10,32 @@ import SwiftUI
import PixelfedKit import PixelfedKit
import ServicesKit import ServicesKit
public class PhotoAttachment: ObservableObject, Identifiable, Equatable, Hashable { @Observable public class PhotoAttachment: Identifiable, Equatable, Hashable {
public let id: String public let id: String
/// Information about image from photos picker. /// Information about image from photos picker.
public let photosPickerItem: PhotosPickerItem? @ObservationIgnored public let photosPickerItem: PhotosPickerItem?
/// Information about image from share extension. /// Information about image from share extension.
public let nsItemProvider: NSItemProvider? @ObservationIgnored public let nsItemProvider: NSItemProvider?
/// Information about image from camera sheet. /// Information about image from camera sheet.
public let uiImage: UIImage? @ObservationIgnored public let uiImage: UIImage?
/// Variable used for presentation layer. /// Variable used for presentation layer.
@Published public var photoData: Data? public var photoData: Data?
/// Property which stores orginal image file copied from Photos to tmp folder. /// Property which stores orginal image file copied from Photos to tmp folder.
@Published public var photoUrl: URL? public var photoUrl: URL?
/// Property stores information after upload to Pixelfed. /// Property stores information after upload to Pixelfed.
@Published public var uploadedAttachment: UploadedAttachment? public var uploadedAttachment: UploadedAttachment?
/// Error from Pixelfed. /// Error from Pixelfed.
@Published public var uploadError: Error? public var uploadError: Error?
/// Error from device. /// Error from device.
@Published public var loadError: Error? public var loadError: Error?
public init(photosPickerItem: PhotosPickerItem? = nil, nsItemProvider: NSItemProvider? = nil, uiImage: UIImage? = nil) { public init(photosPickerItem: PhotosPickerItem? = nil, nsItemProvider: NSItemProvider? = nil, uiImage: UIImage? = nil) {
self.id = UUID().uuidString self.id = UUID().uuidString

View File

@ -11,8 +11,7 @@ import ServicesKit
import ClientKit import ClientKit
import EnvironmentKit import EnvironmentKit
@MainActor @MainActor @Observable public class TextModel: NSObject {
public class TextModel: NSObject, ObservableObject {
public var client: Client? public var client: Client?
public var textView: UITextView? public var textView: UITextView?
@ -37,10 +36,10 @@ public class TextModel: NSObject, ObservableObject {
return textView.markedTextRange return textView.markedTextRange
} }
@Published public var mentionsSuggestions: [Account] = [] public var mentionsSuggestions: [Account] = []
@Published public var tagsSuggestions: [Tag] = [] public var tagsSuggestions: [Tag] = []
@Published public var text = NSMutableAttributedString(string: "") { public var text = NSMutableAttributedString(string: "") {
didSet { didSet {
let range = selectedRange let range = selectedRange
processText() processText()
@ -135,6 +134,7 @@ public class TextModel: NSObject, ObservableObject {
} }
} }
@MainActor
private func loadAutoCompleteResults(query: String) { private func loadAutoCompleteResults(query: String) {
guard let client, query.utf8.count > 1 else { return } guard let client, query.utf8.count > 1 else { return }
var query = query var query = query

View File

@ -12,11 +12,12 @@ import ClientKit
import EnvironmentKit import EnvironmentKit
import ServicesKit import ServicesKit
@MainActor
public struct BaseComposeView: View { public struct BaseComposeView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(Client.self) var client
@StateObject private var textModel: TextModel @State private var textModel: TextModel
@State private var isKeyboardPresented = true @State private var isKeyboardPresented = true
@State private var isSensitive = false @State private var isSensitive = false
@ -93,7 +94,7 @@ public struct BaseComposeView: View {
self.onUpload = onUpload self.onUpload = onUpload
self.draggedItem = nil self.draggedItem = nil
_textModel = StateObject(wrappedValue: .init()) self._textModel = .init(initialValue: .init())
} }
public var body: some View { public var body: some View {

View File

@ -8,6 +8,7 @@ import Foundation
import UIKit import UIKit
import SwiftUI import SwiftUI
@MainActor
struct CameraPickerView: UIViewControllerRepresentable { struct CameraPickerView: UIViewControllerRepresentable {
@Environment(\.presentationMode) var isPresented @Environment(\.presentationMode) var isPresented
@Binding var selectedImage: UIImage? @Binding var selectedImage: UIImage?

View File

@ -7,6 +7,7 @@
import Foundation import Foundation
import SwiftUI import SwiftUI
@MainActor
public struct CustomPageTabViewStyleView<T>: View where T: Identifiable<String> { public struct CustomPageTabViewStyleView<T>: View where T: Identifiable<String> {
@Binding var currentId: String @Binding var currentId: String

View File

@ -8,9 +8,10 @@ import SwiftUI
import ServicesKit import ServicesKit
import EnvironmentKit import EnvironmentKit
@MainActor
public struct ImageUploadView: View { public struct ImageUploadView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@ObservedObject public var photoAttachment: PhotoAttachment public var photoAttachment: PhotoAttachment
private let size: Double private let size: Double
private let delete: () -> Void private let delete: () -> Void

View File

@ -8,12 +8,13 @@ import SwiftUI
import ClientKit import ClientKit
import ServicesKit import ServicesKit
@MainActor
public struct PhotoEditorView: View { public struct PhotoEditorView: View {
@EnvironmentObject var client: Client @Environment(Client.self) var client
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@State private var description: String = String.empty() @State private var description: String = String.empty()
@ObservedObject public var photoAttachment: PhotoAttachment public var photoAttachment: PhotoAttachment
public init(photoAttachment: PhotoAttachment) { public init(photoAttachment: PhotoAttachment) {
self.photoAttachment = photoAttachment self.photoAttachment = photoAttachment

View File

@ -10,9 +10,10 @@ import ClientKit
import ServicesKit import ServicesKit
import EnvironmentKit import EnvironmentKit
@MainActor
public struct PlaceSelectorView: View { public struct PlaceSelectorView: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@EnvironmentObject var client: Client @Environment(Client.self) var client
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@State private var places: [Place] = [] @State private var places: [Place] = []

View File

@ -8,7 +8,7 @@ import SwiftUI
import EnvironmentKit import EnvironmentKit
public struct ImageAlternativeText: View { public struct ImageAlternativeText: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
private let text: String? private let text: String?
private let open: (String) -> Void private let open: (String) -> Void

View File

@ -10,7 +10,7 @@ import NukeUI
import EnvironmentKit import EnvironmentKit
public struct ImageAvatar: View { public struct ImageAvatar: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
private let displayName: String? private let displayName: String?
private let avatarUrl: URL? private let avatarUrl: URL?

View File

@ -9,7 +9,7 @@ import SwiftUI
import EnvironmentKit import EnvironmentKit
public struct ImageFavourite: View { public struct ImageFavourite: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@Binding private var isFavourited: Bool @Binding private var isFavourited: Bool
public init(isFavourited: Binding<Bool>) { public init(isFavourited: Binding<Bool>) {

View File

@ -9,7 +9,7 @@ import ActivityIndicatorView
import EnvironmentKit import EnvironmentKit
public struct LoadingIndicator: View { public struct LoadingIndicator: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
@Binding var isVisible: Bool @Binding var isVisible: Bool
public init(isVisible: Binding<Bool> = .constant(true)) { public init(isVisible: Binding<Bool> = .constant(true)) {

View File

@ -10,7 +10,7 @@ import EmojiText
import EnvironmentKit import EnvironmentKit
public struct MarkdownFormattedText: View { public struct MarkdownFormattedText: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
private let markdown: String private let markdown: String
private let textView = UITextView() private let textView = UITextView()

View File

@ -10,7 +10,7 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
public struct UserAvatar: View { public struct UserAvatar: View {
@EnvironmentObject var applicationState: ApplicationState @Environment(ApplicationState.self) var applicationState
public enum Size { public enum Size {
case mini, list, comment, profile, large case mini, list, comment, profile, large