diff --git a/Shared/Account/AccountModel.swift b/Shared/Account/AccountModel.swift index 4ce81b2..25683a5 100644 --- a/Shared/Account/AccountModel.swift +++ b/Shared/Account/AccountModel.swift @@ -1,66 +1,62 @@ -import Foundation +import SwiftUI import WriteFreely enum AccountError: Error { case invalidPassword case usernameNotFound case serverNotFound } extension AccountError: LocalizedError { public var errorDescription: String? { switch self { case .serverNotFound: return NSLocalizedString( "The server could not be found. Please check the information you've entered and try again.", comment: "" ) case .invalidPassword: return NSLocalizedString( "Invalid password. Please check that you've entered your password correctly and try logging in again.", comment: "" ) case .usernameNotFound: return NSLocalizedString( "Username not found. Did you use your email address by mistake?", comment: "" ) } } } struct AccountModel { + @AppStorage("isLoggedIn") var isLoggedIn: Bool = false private let defaults = UserDefaults.standard - let isLoggedInFlag = "isLoggedInFlag" let usernameStringKey = "usernameStringKey" let serverStringKey = "serverStringKey" var server: String = "" var username: String = "" private(set) var user: WFUser? - private(set) var isLoggedIn: Bool = false mutating func login(_ user: WFUser) { self.user = user self.username = user.username ?? "" self.isLoggedIn = true - defaults.set(true, forKey: isLoggedInFlag) defaults.set(user.username, forKey: usernameStringKey) defaults.set(server, forKey: serverStringKey) } mutating func logout() { self.user = nil self.isLoggedIn = false - defaults.set(false, forKey: isLoggedInFlag) defaults.removeObject(forKey: usernameStringKey) defaults.removeObject(forKey: serverStringKey) } mutating func restoreState() { - isLoggedIn = defaults.bool(forKey: isLoggedInFlag) server = defaults.string(forKey: serverStringKey) ?? "" username = defaults.string(forKey: usernameStringKey) ?? "" } } diff --git a/Shared/Navigation/ContentView.swift b/Shared/Navigation/ContentView.swift index f270ca8..bc5e6ce 100644 --- a/Shared/Navigation/ContentView.swift +++ b/Shared/Navigation/ContentView.swift @@ -1,144 +1,145 @@ import SwiftUI struct ContentView: View { @EnvironmentObject var model: WriteFreelyModel @Binding var sidebarIsHidden: Bool var body: some View { NavigationView { #if os(macOS) SidebarView() .toolbar { Button( action: { NSApp.keyWindow?.contentViewController?.tryToPerform( #selector(NSSplitViewController.toggleSidebar(_:)), with: nil ) withAnimation { self.sidebarIsHidden.toggle() } }, label: { Image(systemName: "sidebar.left") } ) Spacer() Button(action: { withAnimation { self.model.selectedPost = nil } let managedPost = WFAPost(context: LocalStorageManager.persistentContainer.viewContext) managedPost.createdDate = Date() managedPost.title = "" managedPost.body = "" managedPost.status = PostStatus.local.rawValue managedPost.collectionAlias = nil switch model.preferences.font { 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 } withAnimation { self.model.selectedPost = managedPost } }, label: { Image(systemName: "square.and.pencil") }) } #else SidebarView() #endif #if os(macOS) PostListView(selectedCollection: nil, showAllPosts: model.account.isLoggedIn) .toolbar { ToolbarItem(placement: .navigation) { Button(action: { DispatchQueue.main.async { model.fetchUserCollections() model.fetchUserPosts() } }, label: { Image(systemName: "arrow.clockwise") }) - .padding(.leading, sidebarIsHidden ? 8 : 0) - .animation(.linear) + .disabled(!model.account.isLoggedIn) + .padding(.leading, sidebarIsHidden ? 8 : 0) + .animation(.linear) } ToolbarItem(placement: .status) { if let selectedPost = model.selectedPost { PostStatusBadgeView(post: selectedPost) } } ToolbarItemGroup(placement: .primaryAction) { if let selectedPost = model.selectedPost { Button(action: { DispatchQueue.main.async { LocalStorageManager().saveContext() model.publish(post: selectedPost) } }, label: { Image(systemName: "paperplane") }) .disabled(selectedPost.body.isEmpty || selectedPost.status == PostStatus.published.rawValue) Button(action: {}, label: { Image(systemName: "square.and.arrow.up") }) .disabled(selectedPost.status == PostStatus.local.rawValue) } } } #else PostListView(selectedCollection: nil, showAllPosts: model.account.isLoggedIn) #endif Text("Select a post, or create a new local draft.") .foregroundColor(.secondary) } .environmentObject(model) .alert(isPresented: $model.isPresentingDeleteAlert) { Alert( title: Text("Delete Post?"), message: Text("This action cannot be undone."), primaryButton: .destructive(Text("Delete"), action: { if let postToDelete = model.postToDelete { model.selectedPost = nil DispatchQueue.main.async { model.posts.remove(postToDelete) } model.postToDelete = nil } }), secondaryButton: .cancel() { model.postToDelete = nil } ) } .alert(isPresented: $model.isPresentingNetworkErrorAlert, content: { Alert( title: Text("Connection Error"), message: Text("There is no internet connection at the moment. Please reconnect or try again later"), dismissButton: .default(Text("OK"), action: { model.isPresentingNetworkErrorAlert = false }) ) }) #if os(iOS) EmptyView() .sheet( isPresented: $model.isPresentingSettingsView, onDismiss: { model.isPresentingSettingsView = false }, content: { SettingsView() .environmentObject(model) } ) #endif } } struct ContentView_Previews: PreviewProvider { static var previews: some View { let context = LocalStorageManager.persistentContainer.viewContext let model = WriteFreelyModel() return ContentView(sidebarIsHidden: .constant(false)) .environment(\.managedObjectContext, context) .environmentObject(model) } } diff --git a/Shared/WriteFreely_MultiPlatformApp.swift b/Shared/WriteFreely_MultiPlatformApp.swift index 0221e4d..f0918c5 100644 --- a/Shared/WriteFreely_MultiPlatformApp.swift +++ b/Shared/WriteFreely_MultiPlatformApp.swift @@ -1,105 +1,106 @@ import SwiftUI @main struct WriteFreely_MultiPlatformApp: App { @StateObject private var model = WriteFreelyModel() @State private var sidebarIsHidden: Bool = false #if os(macOS) @State private var selectedTab = 0 #endif var body: some Scene { WindowGroup { ContentView(sidebarIsHidden: $sidebarIsHidden) .onAppear(perform: { if let lastDraft = model.editor.fetchLastDraftFromUserDefaults() { self.model.selectedPost = lastDraft } else { createNewLocalPost() } }) .environmentObject(model) .environment(\.managedObjectContext, LocalStorageManager.persistentContainer.viewContext) // .preferredColorScheme(preferences.selectedColorScheme) // See PreferencesModel for info. } .commands { CommandGroup(replacing: .newItem, addition: { Button("New Local Draft") { createNewLocalPost() } .keyboardShortcut("n", modifiers: [.command]) }) CommandGroup(after: .newItem) { Button("Reload From Server") { DispatchQueue.main.async { model.fetchUserCollections() model.fetchUserPosts() } } + .disabled(!model.account.isLoggedIn) .keyboardShortcut("r", modifiers: [.command]) } #if os(macOS) CommandGroup(after: .sidebar) { Button("Toggle Sidebar") { NSApp.keyWindow?.contentViewController?.tryToPerform( #selector(NSSplitViewController.toggleSidebar(_:)), with: nil ) withAnimation { self.sidebarIsHidden.toggle() } } .keyboardShortcut("s", modifiers: [.command, .option]) } #endif } #if os(macOS) Settings { TabView(selection: $selectedTab) { MacAccountView() .environmentObject(model) .tabItem { Image(systemName: "person.crop.circle") Text("Account") } .tag(0) MacPreferencesView(preferences: model.preferences) .tabItem { Image(systemName: "gear") Text("Preferences") } .tag(1) } .frame(minWidth: 300, maxWidth: 300, minHeight: 200, maxHeight: 200) .padding() // .preferredColorScheme(preferences.selectedColorScheme) // See PreferencesModel for info. } #endif } private func createNewLocalPost() { withAnimation { self.model.selectedPost = nil } let managedPost = WFAPost(context: LocalStorageManager.persistentContainer.viewContext) managedPost.createdDate = Date() managedPost.title = "" managedPost.body = "" managedPost.status = PostStatus.local.rawValue managedPost.collectionAlias = nil switch model.preferences.font { 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 } withAnimation { self.model.selectedPost = managedPost } } }