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 PixelfedKit
public class Client: ObservableObject {
@Observable public class Client {
public static let shared = Client()
private init() { }

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@ import SwiftUI
import PixelfedKit
import ClientKit
public class ApplicationState: ObservableObject {
@Observable public class ApplicationState {
public static let shared = ApplicationState()
private init() { }
@ -24,94 +24,94 @@ public class ApplicationState: ObservableObject {
private static let defaults = Defaults()
/// 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.
@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.
@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.
@Published public private(set) var statusCharactersReservedPerUrl = defaults.statusCharactersReservedPerUrl
public private(set) var statusCharactersReservedPerUrl = defaults.statusCharactersReservedPerUrl
/// 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.
@Published public var amountOfNewStatuses = 0
public var amountOfNewStatuses = 0
/// Model for newly created comment.
@Published public var newComment: CommentModel?
public var newComment: CommentModel?
/// Active icon name.
@Published public var activeIcon = "Default"
public var activeIcon = "Default"
/// Tint color in whole application.
@Published public var tintColor = TintColor.accentColor2
public var tintColor = TintColor.accentColor2
/// Application theme.
@Published public var theme = Theme.system
public var theme = Theme.system
/// Avatar shape.
@Published public var avatarShape = AvatarShape.circle
public var avatarShape = AvatarShape.circle
/// Status id for showed interaction row.
@Published public var showInteractionStatusId = ""
public var showInteractionStatusId = ""
/// 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.
@Published public var hapticRefreshEnabled = true
public var hapticRefreshEnabled = true
/// 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.
@Published public var hapticAnimationEnabled = true
public var hapticAnimationEnabled = true
/// Should we fire haptic when notification occures.
@Published public var hapticNotificationEnabled = true
public var hapticNotificationEnabled = true
/// Should sensitive photos without mask.
@Published public var showSensitive = false
public var showSensitive = false
/// Should photo description for visually impaired be displayed.
@Published public var showPhotoDescription = false
public var showPhotoDescription = false
/// Status which should be shown from URL.
@Published public var showStatusId: String?
public var showStatusId: String?
/// Account which should be shown from URL.
@Published public var showAccountId: String?
public var showAccountId: String?
/// Updated user profile.
@Published public var updatedProfile: Account?
public var updatedProfile: Account?
/// 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.
@Published public var showAvatarsOnTimeline = false
public var showAvatarsOnTimeline = false
/// Should favourites be visible on timelines.
@Published public var showFavouritesOnTimeline = false
public var showFavouritesOnTimeline = false
/// 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.
@Published public var warnAboutMissingAlt = true
public var warnAboutMissingAlt = true
/// Show grid of photos on user profile.
@Published public var showGridOnUserProfile = false
public var showGridOnUserProfile = false
/// Show reboosted statuses on home timeline.
@Published public var showReboostedStatuses = false
public var showReboostedStatuses = false
/// Hide statuses without ALT text.
@Published public var hideStatusesWithoutAlt = false
public var hideStatusesWithoutAlt = false
public func changeApplicationState(accountModel: AccountModel, instance: Instance?, lastSeenStatusId: String?) {
self.account = accountModel

View File

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

View File

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

View File

@ -10,14 +10,13 @@ import ServicesKit
import OSLog
import EnvironmentKit
@MainActor
final class TipsStore: ObservableObject {
@Observable final class TipsStore {
/// 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.
@Published private(set) var status: ActionStatus? {
private(set) var status: ActionStatus? {
didSet {
switch status {
case .failed:
@ -29,7 +28,7 @@ final class TipsStore: ObservableObject {
}
/// True when error during purchase occures.
@Published var hasError = false
var hasError = false
/// Error during purchase.
var error: PurchaseError? {

View File

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

View File

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

View File

@ -8,7 +8,7 @@ import SwiftUI
import Foundation
import PixelfedKit
public class RelationshipModel: ObservableObject {
@Observable public class RelationshipModel {
enum RelationshipAction {
case follow
case unfollow
@ -17,46 +17,46 @@ public class RelationshipModel: ObservableObject {
}
/// The account ID.
@Published public var id: EntityId
public var id: EntityId
/// Are you followed by this user?
@Published public var followedBy: Bool
public var followedBy: Bool
/// Is this user blocking you?
@Published public var blockedBy: Bool
public var blockedBy: Bool
/// 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?
@Published public var requested: Bool
public var requested: Bool
/// 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?
@Published public var notifying: Bool
public var notifying: Bool
/// Are you blocking this users domain?
@Published public var domainBlocking: Bool
public var domainBlocking: Bool
/// 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).
@Published public var languages: [String]?
public var languages: [String]?
/// This users profile bio.
@Published public var note: String?
public var note: String?
/// Are you following this user?
@Published public var following: Bool
public var following: Bool
/// Are you blocking this user?
@Published public var blocking: Bool
public var blocking: Bool
/// Are you muting this user?
@Published public var muting: Bool
public var muting: Bool
public init() {
self.id = ""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,32 +10,32 @@ import SwiftUI
import PixelfedKit
import ServicesKit
public class PhotoAttachment: ObservableObject, Identifiable, Equatable, Hashable {
@Observable public class PhotoAttachment: Identifiable, Equatable, Hashable {
public let id: String
/// Information about image from photos picker.
public let photosPickerItem: PhotosPickerItem?
@ObservationIgnored public let photosPickerItem: PhotosPickerItem?
/// Information about image from share extension.
public let nsItemProvider: NSItemProvider?
@ObservationIgnored public let nsItemProvider: NSItemProvider?
/// Information about image from camera sheet.
public let uiImage: UIImage?
@ObservationIgnored public let uiImage: UIImage?
/// 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.
@Published public var photoUrl: URL?
public var photoUrl: URL?
/// Property stores information after upload to Pixelfed.
@Published public var uploadedAttachment: UploadedAttachment?
public var uploadedAttachment: UploadedAttachment?
/// Error from Pixelfed.
@Published public var uploadError: Error?
public var uploadError: Error?
/// Error from device.
@Published public var loadError: Error?
public var loadError: Error?
public init(photosPickerItem: PhotosPickerItem? = nil, nsItemProvider: NSItemProvider? = nil, uiImage: UIImage? = nil) {
self.id = UUID().uuidString

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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