diff --git a/Shared/Extensions/WriteFreelyModel+API.swift b/Shared/Extensions/WriteFreelyModel+API.swift index 3a9010b..1a574ab 100644 --- a/Shared/Extensions/WriteFreelyModel+API.swift +++ b/Shared/Extensions/WriteFreelyModel+API.swift @@ -1,175 +1,182 @@ import Foundation import WriteFreely extension WriteFreelyModel { func login(to server: URL, as username: String, password: String) { if !hasNetworkConnection { self.currentError = NetworkError.noConnectionError return } let secureProtocolPrefix = "https://" let insecureProtocolPrefix = "http://" var serverString = server.absoluteString // If there's neither an http or https prefix, prepend "https://" to the server string. if !(serverString.hasPrefix(secureProtocolPrefix) || serverString.hasPrefix(insecureProtocolPrefix)) { serverString = secureProtocolPrefix + serverString } // If the server string is prefixed with http, upgrade to https before attempting to login. if serverString.hasPrefix(insecureProtocolPrefix) { serverString = serverString.replacingOccurrences(of: insecureProtocolPrefix, with: secureProtocolPrefix) } isLoggingIn = true var serverURL = URL(string: serverString)! if !serverURL.path.isEmpty { serverURL.deleteLastPathComponent() } account.server = serverURL.absoluteString client = WFClient(for: serverURL) client?.login(username: username, password: password, completion: loginHandler) } func logout() { if !hasNetworkConnection { self.currentError = NetworkError.noConnectionError return } guard let loggedInClient = client else { do { try purgeTokenFromKeychain(username: account.username, server: account.server) account.logout() } catch { self.currentError = KeychainError.couldNotPurgeAccessToken } return } loggedInClient.logout(completion: logoutHandler) } func fetchUserCollections() { if !hasNetworkConnection { self.currentError = NetworkError.noConnectionError return } guard let loggedInClient = client else { self.currentError = AppError.couldNotGetLoggedInClient return } // We're starting the network request. DispatchQueue.main.async { self.isProcessingRequest = true } loggedInClient.getUserCollections(completion: fetchUserCollectionsHandler) } func fetchUserPosts() { if !hasNetworkConnection { self.currentError = NetworkError.noConnectionError return } guard let loggedInClient = client else { self.currentError = AppError.couldNotGetLoggedInClient return } // We're starting the network request. DispatchQueue.main.async { self.isProcessingRequest = true } loggedInClient.getPosts(completion: fetchUserPostsHandler) } func publish(post: WFAPost) { postToUpdate = nil if !hasNetworkConnection { self.currentError = NetworkError.noConnectionError return } guard let loggedInClient = client else { self.currentError = AppError.couldNotGetLoggedInClient return } // We're starting the network request. DispatchQueue.main.async { self.isProcessingRequest = true } if post.language == nil { - if let languageCode = Locale.current.languageCode { - post.language = languageCode - post.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft + if #available(iOS 16, macOS 13, *) { + if let languageCode = Locale.current.language.languageCode?.identifier { + post.language = languageCode + post.rtl = Locale.Language(identifier: languageCode).characterDirection == .rightToLeft + } + } else { + if let languageCode = Locale.current.languageCode { + post.language = languageCode + post.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft + } } } var wfPost = WFPost( body: post.body, title: post.title.isEmpty ? "" : post.title, appearance: post.appearance, language: post.language, rtl: post.rtl, createdDate: post.status == PostStatus.local.rawValue ? Date() : post.createdDate ) if let existingPostId = post.postId { // This is an existing post. postToUpdate = post wfPost.postId = post.postId loggedInClient.updatePost( postId: existingPostId, updatedPost: wfPost, completion: publishHandler ) } else { // This is a new local draft. loggedInClient.createPost( post: wfPost, in: post.collectionAlias, completion: publishHandler ) } } func updateFromServer(post: WFAPost) { if !hasNetworkConnection { self.currentError = NetworkError.noConnectionError return } guard let loggedInClient = client else { self.currentError = AppError.couldNotGetLoggedInClient return } guard let postId = post.postId else { self.currentError = AppError.couldNotGetPostId return } // We're starting the network request. DispatchQueue.main.async { #if os(iOS) self.selectedPost = post #endif self.isProcessingRequest = true } loggedInClient.getPost(byId: postId, completion: updateFromServerHandler) } func move(post: WFAPost, from oldCollection: WFACollection?, to newCollection: WFACollection?) { if !hasNetworkConnection { self.currentError = NetworkError.noConnectionError return } guard let loggedInClient = client else { self.currentError = AppError.couldNotGetLoggedInClient return } guard let postId = post.postId else { self.currentError = AppError.couldNotGetPostId return } // We're starting the network request. DispatchQueue.main.async { self.isProcessingRequest = true } selectedPost = post post.collectionAlias = newCollection?.alias loggedInClient.movePost(postId: postId, to: newCollection?.alias, completion: movePostHandler) } } diff --git a/Shared/PostEditor/PostEditorModel.swift b/Shared/PostEditor/PostEditorModel.swift index 319beaa..c6fda4c 100644 --- a/Shared/PostEditor/PostEditorModel.swift +++ b/Shared/PostEditor/PostEditorModel.swift @@ -1,85 +1,92 @@ import SwiftUI import CoreData enum PostAppearance: String { case sans = "OpenSans-Regular" case mono = "Hack-Regular" case serif = "Lora-Regular" } struct PostEditorModel { @AppStorage(WFDefaults.showAllPostsFlag, store: UserDefaults.shared) var showAllPostsFlag: Bool = false @AppStorage(WFDefaults.selectedCollectionURL, store: UserDefaults.shared) var selectedCollectionURL: URL? @AppStorage(WFDefaults.lastDraftURL, store: UserDefaults.shared) var lastDraftURL: URL? private(set) var initialPostTitle: String? private(set) var initialPostBody: String? #if os(macOS) var postToUpdate: WFAPost? #endif func saveLastDraft(_ post: WFAPost) { self.lastDraftURL = post.status != PostStatus.published.rawValue ? post.objectID.uriRepresentation() : nil } func clearLastDraft() { self.lastDraftURL = nil } func fetchLastDraftFromAppStorage() -> WFAPost? { guard let postURL = lastDraftURL else { return nil } guard let post = fetchManagedObject(from: postURL) as? WFAPost else { return nil } return post } func generateNewLocalPost(withFont appearance: Int) -> WFAPost { let managedPost = WFAPost(context: LocalStorageManager.standard.container.viewContext) managedPost.createdDate = Date() managedPost.title = "" managedPost.body = "" managedPost.status = PostStatus.local.rawValue managedPost.collectionAlias = WriteFreelyModel.shared.selectedCollection?.alias switch appearance { case 1: managedPost.appearance = "sans" case 2: managedPost.appearance = "wrap" default: managedPost.appearance = "serif" } - if let languageCode = Locale.current.languageCode { - managedPost.language = languageCode - managedPost.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft + if #available(iOS 16, macOS 13, *) { + if let languageCode = Locale.current.language.languageCode?.identifier { + managedPost.language = languageCode + managedPost.rtl = Locale.Language(identifier: languageCode).characterDirection == .rightToLeft + } + } else { + if let languageCode = Locale.current.languageCode { + managedPost.language = languageCode + managedPost.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft + } } return managedPost } func fetchSelectedCollectionFromAppStorage() -> WFACollection? { guard let collectionURL = selectedCollectionURL else { return nil } guard let collection = fetchManagedObject(from: collectionURL) as? WFACollection else { return nil } return collection } /// Sets the initial values for title and body on a published post. /// /// Used to detect if the title and body have changed back to their initial values. If the passed `WFAPost` isn't /// published, any title and post values already stored are reset to `nil`. /// - Parameter post: The `WFAPost` for which we're setting initial title/body values. mutating func setInitialValues(for post: WFAPost) { if post.status != PostStatus.published.rawValue { initialPostTitle = nil initialPostBody = nil return } initialPostTitle = post.title initialPostBody = post.body } private func fetchManagedObject(from objectURL: URL) -> NSManagedObject? { let coordinator = LocalStorageManager.standard.container.persistentStoreCoordinator guard let managedObjectID = coordinator.managedObjectID(forURIRepresentation: objectURL) else { return nil } let object = LocalStorageManager.standard.container.viewContext.object(with: managedObjectID) return object } }