diff --git a/CHANGELOG.md b/CHANGELOG.md index 73f0bd4..8e41ea9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,44 +1,45 @@ # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Added - Post editor now has a Publish button to change post status - Collections sidebar to choose a specific collection (i.e., blog) +- Settings to provide the user interface for logging in, setting preferred color scheme ### Changed - Updated license from AGPLv3 to GPLv3 ## [0.0.2] - 2020-07-30 ### Added - Basic post list for displaying (local) posts - Basic post editor for: - Creating a new local draft (title and content only) - Updating a (local) post - Badge for post status (`draft`, `edited`, `published`) ## [0.0.1] - 2020-07-22 ### Added - WriteFreely Swift package - SwiftLint build phase for both macOS and iOS targets - Project metadocuments, including: - Project readme - APGL v3 license - Code of conduct - Contributing guide - This changelog [Unreleased]: https://github.com/writeas/writefreely-swiftui-multiplatform/compare/v0.0.2...HEAD [0.0.2]: https://github.com/writeas/writefreely-swiftui-multiplatform/compare/v0.0.1...v0.0.2 [0.0.1]: https://github.com/writeas/writefreely-swiftui-multiplatform/releases/tag/v0.0.1 diff --git a/Shared/Account/AccountLoginView.swift b/Shared/Account/AccountLoginView.swift new file mode 100644 index 0000000..adc331f --- /dev/null +++ b/Shared/Account/AccountLoginView.swift @@ -0,0 +1,110 @@ +import SwiftUI + +struct AccountLoginView: View { + @ObservedObject var account: AccountModel + + @State private var isShowingAlert: Bool = false + @State private var alertMessage: String = "" + + var body: some View { + VStack { + HStack { + Image(systemName: "person.circle") + .foregroundColor(.gray) + #if os(iOS) + TextField("Username", text: $account.username) + .autocapitalization(.none) + .disableAutocorrection(true) + .textFieldStyle(RoundedBorderTextFieldStyle()) + #else + TextField("Username", text: $account.username) + #endif + } + HStack { + Image(systemName: "lock.circle") + .foregroundColor(.gray) + #if os(iOS) + SecureField("Password", text: $account.password) + .autocapitalization(.none) + .disableAutocorrection(true) + .textFieldStyle(RoundedBorderTextFieldStyle()) + #else + SecureField("Password", text: $account.password) + #endif + } + HStack { + Image(systemName: "link.circle") + .foregroundColor(.gray) + #if os(iOS) + TextField("Server URL", text: $account.server) + .keyboardType(.URL) + .autocapitalization(.none) + .disableAutocorrection(true) + .textFieldStyle(RoundedBorderTextFieldStyle()) + #else + TextField("Server URL", text: $account.server) + #endif + } + Spacer() + if account.isLoggingIn { + ProgressView("Logging in...") + .padding() + } else { + Button(action: { + account.login( + to: account.server, + as: account.username, password: account.password, + completion: loginHandler + ) + }, label: { + Text("Login") + }) + .disabled(account.isLoggedIn) + .padding() + } + } + .alert(isPresented: $isShowingAlert) { + Alert( + title: Text("Error Logging In"), + message: Text(alertMessage), + dismissButton: .default(Text("OK")) + ) + } + } + + func loginHandler(result: Result) { + do { + _ = try result.get() + } catch AccountError.serverNotFound { + alertMessage = """ + The server could not be found. Please check that you've entered the information correctly and try again. + """ + isShowingAlert = true + } catch AccountError.invalidPassword { + alertMessage = """ + Invalid password. Please check that you've entered your password correctly and try logging in again. + """ + isShowingAlert = true + } catch AccountError.usernameNotFound { + alertMessage = """ + Username not found. Did you use your email address by mistake? + """ + isShowingAlert = true + } catch { + alertMessage = "An unknown error occurred. Please try again." + isShowingAlert = true + } + } +} + +struct AccountLoginView_LoggedOutPreviews: PreviewProvider { + static var previews: some View { + AccountLoginView(account: AccountModel()) + } +} + +struct AccountLoginView_LoggingInPreviews: PreviewProvider { + static var previews: some View { + AccountLoginView(account: AccountModel()) + } +} diff --git a/Shared/Account/AccountLogoutView.swift b/Shared/Account/AccountLogoutView.swift new file mode 100644 index 0000000..bc84b50 --- /dev/null +++ b/Shared/Account/AccountLogoutView.swift @@ -0,0 +1,29 @@ +import SwiftUI + +struct AccountLogoutView: View { + @ObservedObject var account: AccountModel + + var body: some View { + VStack { + Spacer() + VStack { + Text("Logged in as \(account.username)") + Text("on \(account.server)") + } + Spacer() + Button(action: logoutHandler, label: { + Text("Logout") + }) + } + } + + func logoutHandler() { + account.logout() + } +} + +struct AccountLogoutView_Previews: PreviewProvider { + static var previews: some View { + AccountLogoutView(account: AccountModel()) + } +} diff --git a/Shared/Account/AccountModel.swift b/Shared/Account/AccountModel.swift new file mode 100644 index 0000000..9d26b3c --- /dev/null +++ b/Shared/Account/AccountModel.swift @@ -0,0 +1,71 @@ +import Foundation + +enum AccountError: Error { + case invalidPassword + case usernameNotFound + case serverNotFound +} + +class AccountModel: ObservableObject { + @Published private(set) var id: UUID? + @Published private(set) var isLoggedIn: Bool = false + @Published private(set) var isLoggingIn: Bool = false + @Published var username: String = "" + @Published var password: String = "" + @Published var server: String = "" + + func login( + to server: String, + as username: String, + password: String, + completion: @escaping (Result) -> Void + ) { + self.isLoggingIn = true + let result: Result + + if server != validServer { + result = .failure(.serverNotFound) + } else if username != validCredentials["username"] { + result = .failure(.usernameNotFound) + } else if password != validCredentials["password"] { + result = .failure(.invalidPassword) + } else { + self.id = UUID() + self.username = username + self.password = password + self.server = server + result = .success(self.id!) + } + + #if DEBUG + // Delay to simulate async network call + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + self.isLoggingIn = false + do { + _ = try result.get() + self.isLoggedIn = true + } catch { + self.isLoggedIn = false + } + completion(result) + } + #endif + } + + func logout() { + id = nil + isLoggedIn = false + isLoggingIn = false + username = "" + password = "" + server = "" + } +} + +#if DEBUG +let validCredentials = [ + "username": "test-writer", + "password": "12345" +] +let validServer = "https://test.server.url" +#endif diff --git a/Shared/Account/AccountView.swift b/Shared/Account/AccountView.swift new file mode 100644 index 0000000..a0382d7 --- /dev/null +++ b/Shared/Account/AccountView.swift @@ -0,0 +1,25 @@ +import SwiftUI + +struct AccountView: View { + @ObservedObject var account: AccountModel + + var body: some View { + if account.isLoggedIn { + HStack { + Spacer() + AccountLogoutView(account: account) + Spacer() + } + .padding() + } else { + AccountLoginView(account: account) + .padding() + } + } +} + +struct AccountLogin_Previews: PreviewProvider { + static var previews: some View { + AccountView(account: AccountModel()) + } +} diff --git a/Shared/Navigation/ContentView.swift b/Shared/Navigation/ContentView.swift index 9fb9599..c92177a 100644 --- a/Shared/Navigation/ContentView.swift +++ b/Shared/Navigation/ContentView.swift @@ -1,23 +1,27 @@ import SwiftUI struct ContentView: View { @ObservedObject var postStore: PostStore + @ObservedObject var preferences: PreferencesModel + @ObservedObject var account: AccountModel var body: some View { NavigationView { - CollectionSidebar() + SidebarView() PostList(selectedCollection: allPostsCollection) Text("Select a post, or create a new draft.") .foregroundColor(.secondary) } .environmentObject(postStore) + .environmentObject(preferences) + .environmentObject(account) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { - ContentView(postStore: testPostStore) + ContentView(postStore: testPostStore, preferences: PreferencesModel(), account: AccountModel()) } } diff --git a/Shared/Navigation/SidebarView.swift b/Shared/Navigation/SidebarView.swift new file mode 100644 index 0000000..41d67d7 --- /dev/null +++ b/Shared/Navigation/SidebarView.swift @@ -0,0 +1,13 @@ +import SwiftUI + +struct SidebarView: View { + var body: some View { + CollectionListView() + } +} + +struct SidebarView_Previews: PreviewProvider { + static var previews: some View { + SidebarView() + } +} diff --git a/Shared/Post/PostCell.swift b/Shared/Post/PostCell.swift index 36d17d0..706ba04 100644 --- a/Shared/Post/PostCell.swift +++ b/Shared/Post/PostCell.swift @@ -1,36 +1,37 @@ import SwiftUI struct PostCell: View { @EnvironmentObject var postStore: PostStore + @ObservedObject var post: Post var body: some View { HStack { VStack(alignment: .leading) { Text(post.title) .font(.headline) .lineLimit(1) Text(buildDateString(from: post.createdDate)) .font(.caption) .lineLimit(1) } Spacer() PostStatusBadge(post: post) } .padding(5) } func buildDateString(from date: Date) -> String { let dateFormatter = DateFormatter() dateFormatter.dateStyle = .long dateFormatter.timeStyle = .short return dateFormatter.string(from: date) } } struct PostCell_Previews: PreviewProvider { static var previews: some View { PostCell(post: testPost) } } diff --git a/Shared/Post/PostEditor.swift b/Shared/Post/PostEditor.swift index 57fe7fb..1641f84 100644 --- a/Shared/Post/PostEditor.swift +++ b/Shared/Post/PostEditor.swift @@ -1,71 +1,73 @@ import SwiftUI struct PostEditor: View { @EnvironmentObject var postStore: PostStore + @ObservedObject var post: Post + @State private var isNewPost = false var body: some View { VStack { TextEditor(text: $post.title) .font(.title) .frame(height: 100) .onChange(of: post.title) { _ in if post.status == .published { post.status = .edited } } TextEditor(text: $post.body) .font(.body) .onChange(of: post.body) { _ in if post.status == .published { post.status = .edited } } } .padding() .toolbar { ToolbarItem(placement: .status) { PostStatusBadge(post: post) } ToolbarItem(placement: .primaryAction) { Button(action: { post.status = .published }, label: { Image(systemName: "paperplane") }) } } .onAppear(perform: { checkIfNewPost() if self.isNewPost { addNewPostToStore() } }) } private func checkIfNewPost() { self.isNewPost = !postStore.posts.contains(where: { $0.id == post.id }) } private func addNewPostToStore() { withAnimation { postStore.add(post) self.isNewPost = false } } } struct PostEditor_NewDraftPreviews: PreviewProvider { static var previews: some View { PostEditor(post: Post()) .environmentObject(testPostStore) } } struct PostEditor_ExistingPostPreviews: PreviewProvider { static var previews: some View { PostEditor(post: testPostData[0]) .environmentObject(testPostStore) } } diff --git a/Shared/Post/PostList.swift b/Shared/Post/PostList.swift index 8577636..75fae23 100644 --- a/Shared/Post/PostList.swift +++ b/Shared/Post/PostList.swift @@ -1,91 +1,108 @@ import SwiftUI struct PostList: View { @EnvironmentObject var postStore: PostStore + @State var selectedCollection: PostCollection + @State var isPresentingSettings = false var body: some View { #if os(iOS) - List { - ForEach(showPosts(for: selectedCollection)) { post in - NavigationLink( - destination: PostEditor(post: post) - ) { - PostCell( - post: post - ) + GeometryReader { geometry in + List { + ForEach(showPosts(for: selectedCollection)) { post in + NavigationLink( + destination: PostEditor(post: post) + ) { + PostCell( + post: post + ) + } } } - } - .navigationTitle(selectedCollection.title) - .toolbar { - ToolbarItem(placement: .primaryAction) { - Button(action: { - let post = Post() - postStore.add(post) - }, label: { - Image(systemName: "square.and.pencil") - }) - } - ToolbarItem(placement: .bottomBar) { - Spacer() - } - ToolbarItem(placement: .bottomBar) { - Text(pluralizedPostCount(for: showPosts(for: selectedCollection))) - } - ToolbarItem(placement: .bottomBar) { - Spacer() + .navigationTitle(selectedCollection.title) + .toolbar { + ToolbarItem(placement: .primaryAction) { + Button(action: { + let post = Post() + postStore.add(post) + }, label: { + Image(systemName: "square.and.pencil") + }) + } + ToolbarItem(placement: .bottomBar) { + HStack { + Button(action: { + isPresentingSettings = true + }, label: { + Image(systemName: "gear") + }).sheet( + isPresented: $isPresentingSettings, + onDismiss: { + isPresentingSettings = false + }, + content: { + SettingsView(isPresented: $isPresentingSettings) + } + ) + Spacer() + Text(pluralizedPostCount(for: showPosts(for: selectedCollection))) + .foregroundColor(.secondary) + } + .padding() + .frame(width: geometry.size.width) + } } } #else //if os(macOS) List { ForEach(showPosts(for: selectedCollection)) { post in NavigationLink( destination: PostEditor(post: post) ) { PostCell( post: post ) } } } .navigationTitle(selectedCollection.title) .navigationSubtitle(pluralizedPostCount(for: showPosts(for: selectedCollection))) .toolbar { Button(action: { let post = Post() postStore.add(post) }, label: { Image(systemName: "square.and.pencil") }) } #endif } private func pluralizedPostCount(for posts: [Post]) -> String { if posts.count == 1 { return "1 post" } else { return "\(posts.count) posts" } } private func showPosts(for collection: PostCollection) -> [Post] { if collection == allPostsCollection { return postStore.posts } else { return postStore.posts.filter { $0.collection.title == collection.title } } } } struct PostList_Previews: PreviewProvider { static var previews: some View { Group { PostList(selectedCollection: allPostsCollection) .environmentObject(testPostStore) } } } diff --git a/Shared/PostCollection/CollectionSidebar.swift b/Shared/PostCollection/CollectionListView.swift similarity index 89% rename from Shared/PostCollection/CollectionSidebar.swift rename to Shared/PostCollection/CollectionListView.swift index f327128..0609bfd 100644 --- a/Shared/PostCollection/CollectionSidebar.swift +++ b/Shared/PostCollection/CollectionListView.swift @@ -1,25 +1,25 @@ import SwiftUI -struct CollectionSidebar: View { +struct CollectionListView: View { private let collections = postCollections var body: some View { List { ForEach(collections) { collection in NavigationLink( destination: PostList(selectedCollection: collection) ) { Text(collection.title) } } } .navigationTitle("Collections") .listStyle(SidebarListStyle()) } } struct CollectionSidebar_Previews: PreviewProvider { static var previews: some View { - CollectionSidebar() + CollectionListView() } } diff --git a/Shared/Preferences/PreferencesModel.swift b/Shared/Preferences/PreferencesModel.swift new file mode 100644 index 0000000..39e9c24 --- /dev/null +++ b/Shared/Preferences/PreferencesModel.swift @@ -0,0 +1,54 @@ +import SwiftUI + +class PreferencesModel: ObservableObject { + /* We're stuck dropping into AppKit/UIKit to set light/dark schemes for now, + * because setting the .preferredColorScheme modifier on views in SwiftUI is + * currently unreliable. + * + * Feedback submitted to Apple: + * + * FB8382883: "On macOS 11β4, preferredColorScheme modifier does not respect .light ColorScheme" + * FB8383053: "On iOS 14β4/macOS 11β4, it is not possible to unset preferredColorScheme after setting + * it to either .light or .dark" + */ + + #if os(iOS) + var window: UIWindow? { + guard let scene = UIApplication.shared.connectedScenes.first, + let windowSceneDelegate = scene.delegate as? UIWindowSceneDelegate, + let window = windowSceneDelegate.window else { + return nil + } + return window + } + #endif + + @Published var selectedColorScheme: ColorScheme? + @Published var appearance: Int = 0 { + didSet { + switch appearance { + case 1: +// selectedColorScheme = .light + #if os(macOS) + NSApp.appearance = NSAppearance(named: .aqua) + #else + window?.overrideUserInterfaceStyle = .light + #endif + case 2: +// selectedColorScheme = .dark + #if os(macOS) + NSApp.appearance = NSAppearance(named: .darkAqua) + #else + window?.overrideUserInterfaceStyle = .dark + #endif + default: +// selectedColorScheme = .none + #if os(macOS) + NSApp.appearance = nil + #else + window?.overrideUserInterfaceStyle = .unspecified + #endif + } + } + } +} diff --git a/Shared/Preferences/PreferencesView.swift b/Shared/Preferences/PreferencesView.swift new file mode 100644 index 0000000..26168e3 --- /dev/null +++ b/Shared/Preferences/PreferencesView.swift @@ -0,0 +1,28 @@ +import SwiftUI + +struct PreferencesView: View { + @ObservedObject var preferences: PreferencesModel + + var body: some View { + #if os(iOS) + Picker(selection: $preferences.appearance, label: Text("Appearance")) { + Text("System").tag(0) + Text("Light").tag(1) + Text("Dark").tag(2) + } + .pickerStyle(SegmentedPickerStyle()) + #elseif os(macOS) + Picker(selection: $preferences.appearance, label: Text("Appearance")) { + Text("System").tag(0) + Text("Light").tag(1) + Text("Dark").tag(2) + } + #endif + } +} + +struct SwiftUIView_Previews: PreviewProvider { + static var previews: some View { + PreferencesView(preferences: PreferencesModel()) + } +} diff --git a/Shared/WriteFreely_MultiPlatformApp.swift b/Shared/WriteFreely_MultiPlatformApp.swift index 2febee0..891f8f2 100644 --- a/Shared/WriteFreely_MultiPlatformApp.swift +++ b/Shared/WriteFreely_MultiPlatformApp.swift @@ -1,16 +1,46 @@ import SwiftUI @main struct WriteFreely_MultiPlatformApp: App { + @StateObject private var preferences = PreferencesModel() + @StateObject private var account = AccountModel() + + #if os(macOS) + @State private var selectedTab = 0 + #endif + #if DEBUG @StateObject private var store = testPostStore #else @StateObject private var store = PostStore() #endif var body: some Scene { WindowGroup { - ContentView(postStore: store) + ContentView(postStore: store, preferences: preferences, account: account) +// .preferredColorScheme(preferences.selectedColorScheme) // See PreferencesModel for info. + } + + #if os(macOS) + Settings { + TabView(selection: $selectedTab) { + MacAccountView(account: account) + .tabItem { + Image(systemName: "person.crop.circle") + Text("Account") + } + .tag(0) + MacPreferencesView(preferences: 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 } } diff --git a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj index 45b969e..3b1fbe8 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj +++ b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj @@ -1,895 +1,971 @@ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 52; objects = { /* Begin PBXBuildFile section */ + 17120DA124E19839002B9F6C /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388D24DDEC7400DEFF9A /* AccountView.swift */; }; + 17120DA224E1985C002B9F6C /* AccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388B24DDC83F00DEFF9A /* AccountModel.swift */; }; + 17120DA324E19A42002B9F6C /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5389124DDED0000DEFF9A /* PreferencesView.swift */; }; + 17120DA724E19D11002B9F6C /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17120DA424E19CBF002B9F6C /* SettingsView.swift */; }; + 17120DA924E1B2F5002B9F6C /* AccountLogoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17120DA824E1B2F5002B9F6C /* AccountLogoutView.swift */; }; + 17120DAA24E1B2F5002B9F6C /* AccountLogoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17120DA824E1B2F5002B9F6C /* AccountLogoutView.swift */; }; + 17120DAC24E1B99F002B9F6C /* AccountLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17120DAB24E1B99F002B9F6C /* AccountLoginView.swift */; }; + 17120DAD24E1B99F002B9F6C /* AccountLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17120DAB24E1B99F002B9F6C /* AccountLoginView.swift */; }; + 17120DB224E1E19C002B9F6C /* SettingsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17120DB124E1E19C002B9F6C /* SettingsHeaderView.swift */; }; 171BFDF724D49FD400888236 /* PostCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF624D49FD400888236 /* PostCollection.swift */; }; 171BFDF824D49FD400888236 /* PostCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF624D49FD400888236 /* PostCollection.swift */; }; - 171BFDFA24D4AF8300888236 /* CollectionSidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF924D4AF8300888236 /* CollectionSidebar.swift */; }; - 171BFDFB24D4AF8300888236 /* CollectionSidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF924D4AF8300888236 /* CollectionSidebar.swift */; }; + 171BFDFA24D4AF8300888236 /* CollectionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF924D4AF8300888236 /* CollectionListView.swift */; }; + 171BFDFB24D4AF8300888236 /* CollectionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF924D4AF8300888236 /* CollectionListView.swift */; }; + 1753F6AC24E431CC00309365 /* MacPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1753F6AB24E431CC00309365 /* MacPreferencesView.swift */; }; 1756AE6B24CB1E4B00FD7257 /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE6A24CB1E4B00FD7257 /* Post.swift */; }; 1756AE6C24CB1E4B00FD7257 /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE6A24CB1E4B00FD7257 /* Post.swift */; }; 1756AE6E24CB255B00FD7257 /* PostStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE6D24CB255B00FD7257 /* PostStore.swift */; }; 1756AE6F24CB255B00FD7257 /* PostStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE6D24CB255B00FD7257 /* PostStore.swift */; }; 1756AE7424CB26FA00FD7257 /* PostCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE7324CB26FA00FD7257 /* PostCell.swift */; }; 1756AE7524CB26FA00FD7257 /* PostCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE7324CB26FA00FD7257 /* PostCell.swift */; }; 1756AE7724CB2EDD00FD7257 /* PostEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE7624CB2EDD00FD7257 /* PostEditor.swift */; }; 1756AE7824CB2EDD00FD7257 /* PostEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE7624CB2EDD00FD7257 /* PostEditor.swift */; }; 1756AE7A24CB65DF00FD7257 /* PostList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE7924CB65DF00FD7257 /* PostList.swift */; }; 1756AE7B24CB65DF00FD7257 /* PostList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE7924CB65DF00FD7257 /* PostList.swift */; }; 1756AE8124CB844500FD7257 /* View+Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE8024CB844500FD7257 /* View+Keyboard.swift */; }; + 1765F62A24E18EA200C9EBF0 /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1765F62924E18EA200C9EBF0 /* SidebarView.swift */; }; + 1765F62B24E18EA200C9EBF0 /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1765F62924E18EA200C9EBF0 /* SidebarView.swift */; }; + 17A5388824DDA31F00DEFF9A /* MacAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388724DDA31F00DEFF9A /* MacAccountView.swift */; }; + 17A5388C24DDC83F00DEFF9A /* AccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388B24DDC83F00DEFF9A /* AccountModel.swift */; }; + 17A5388F24DDEC7400DEFF9A /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388D24DDEC7400DEFF9A /* AccountView.swift */; }; + 17A5389324DDED0000DEFF9A /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5389124DDED0000DEFF9A /* PreferencesView.swift */; }; + 17D435E824E3128F0036B539 /* PreferencesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D435E724E3128F0036B539 /* PreferencesModel.swift */; }; + 17D435E924E3128F0036B539 /* PreferencesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D435E724E3128F0036B539 /* PreferencesModel.swift */; }; 17DF329D24C87D3500BCE2E3 /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF329C24C87D3500BCE2E3 /* Tests_iOS.swift */; }; 17DF32A824C87D3500BCE2E3 /* Tests_macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF32A724C87D3500BCE2E3 /* Tests_macOS.swift */; }; 17DF32AA24C87D3500BCE2E3 /* WriteFreely_MultiPlatformApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF328124C87D3300BCE2E3 /* WriteFreely_MultiPlatformApp.swift */; }; 17DF32AB24C87D3500BCE2E3 /* WriteFreely_MultiPlatformApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF328124C87D3300BCE2E3 /* WriteFreely_MultiPlatformApp.swift */; }; 17DF32AC24C87D3500BCE2E3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF328224C87D3300BCE2E3 /* ContentView.swift */; }; 17DF32AD24C87D3500BCE2E3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF328224C87D3300BCE2E3 /* ContentView.swift */; }; 17DF32AE24C87D3500BCE2E3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 17DF328324C87D3500BCE2E3 /* Assets.xcassets */; }; 17DF32AF24C87D3500BCE2E3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 17DF328324C87D3500BCE2E3 /* Assets.xcassets */; }; 17DF32C024C87D7B00BCE2E3 /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 17DF32BF24C87D7B00BCE2E3 /* WriteFreely */; }; 17DF32C324C87D8D00BCE2E3 /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 17DF32C224C87D8D00BCE2E3 /* WriteFreely */; }; 17DF32D524C8CA3400BCE2E3 /* PostStatusBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF32D424C8CA3400BCE2E3 /* PostStatusBadge.swift */; }; 17DF32D624C8CA3400BCE2E3 /* PostStatusBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF32D424C8CA3400BCE2E3 /* PostStatusBadge.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 17DF329924C87D3500BCE2E3 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 17DF327C24C87D3300BCE2E3 /* Project object */; proxyType = 1; remoteGlobalIDString = 17DF328724C87D3500BCE2E3; remoteInfo = "WriteFreely-MultiPlatform (iOS)"; }; 17DF32A424C87D3500BCE2E3 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 17DF327C24C87D3300BCE2E3 /* Project object */; proxyType = 1; remoteGlobalIDString = 17DF328F24C87D3500BCE2E3; remoteInfo = "WriteFreely-MultiPlatform (macOS)"; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 17120DA424E19CBF002B9F6C /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; + 17120DA824E1B2F5002B9F6C /* AccountLogoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountLogoutView.swift; sourceTree = ""; }; + 17120DAB24E1B99F002B9F6C /* AccountLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountLoginView.swift; sourceTree = ""; }; + 17120DB124E1E19C002B9F6C /* SettingsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsHeaderView.swift; sourceTree = ""; }; 171BFDF624D49FD400888236 /* PostCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCollection.swift; sourceTree = ""; }; - 171BFDF924D4AF8300888236 /* CollectionSidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionSidebar.swift; sourceTree = ""; }; + 171BFDF924D4AF8300888236 /* CollectionListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionListView.swift; sourceTree = ""; }; + 1753F6AB24E431CC00309365 /* MacPreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacPreferencesView.swift; sourceTree = ""; }; 1756AE6A24CB1E4B00FD7257 /* Post.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = ""; }; 1756AE6D24CB255B00FD7257 /* PostStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostStore.swift; sourceTree = ""; }; 1756AE7324CB26FA00FD7257 /* PostCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCell.swift; sourceTree = ""; }; 1756AE7624CB2EDD00FD7257 /* PostEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostEditor.swift; sourceTree = ""; }; 1756AE7924CB65DF00FD7257 /* PostList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostList.swift; sourceTree = ""; }; 1756AE8024CB844500FD7257 /* View+Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Keyboard.swift"; sourceTree = ""; }; + 1765F62924E18EA200C9EBF0 /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = ""; }; + 17A5388724DDA31F00DEFF9A /* MacAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAccountView.swift; sourceTree = ""; }; + 17A5388B24DDC83F00DEFF9A /* AccountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountModel.swift; sourceTree = ""; }; + 17A5388D24DDEC7400DEFF9A /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = ""; }; + 17A5389124DDED0000DEFF9A /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = ""; }; + 17D435E724E3128F0036B539 /* PreferencesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesModel.swift; sourceTree = ""; }; 17DF328124C87D3300BCE2E3 /* WriteFreely_MultiPlatformApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteFreely_MultiPlatformApp.swift; sourceTree = ""; }; 17DF328224C87D3300BCE2E3 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 17DF328324C87D3500BCE2E3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 17DF328824C87D3500BCE2E3 /* WriteFreely-MultiPlatform.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "WriteFreely-MultiPlatform.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 17DF328B24C87D3500BCE2E3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 17DF329024C87D3500BCE2E3 /* WriteFreely-MultiPlatform.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "WriteFreely-MultiPlatform.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 17DF329224C87D3500BCE2E3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 17DF329324C87D3500BCE2E3 /* macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS.entitlements; sourceTree = ""; }; 17DF329824C87D3500BCE2E3 /* Tests iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 17DF329C24C87D3500BCE2E3 /* Tests_iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_iOS.swift; sourceTree = ""; }; 17DF329E24C87D3500BCE2E3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 17DF32A324C87D3500BCE2E3 /* Tests macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 17DF32A724C87D3500BCE2E3 /* Tests_macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOS.swift; sourceTree = ""; }; 17DF32A924C87D3500BCE2E3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 17DF32C624C884FF00BCE2E3 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 17DF32C724C8853700BCE2E3 /* CODE_OF_CONDUCT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CODE_OF_CONDUCT.md; sourceTree = ""; }; 17DF32C824C8854B00BCE2E3 /* CONTRIBUTING.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CONTRIBUTING.md; sourceTree = ""; }; 17DF32C924C8855E00BCE2E3 /* LICENSE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = ""; }; 17DF32CA24C8856C00BCE2E3 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; 17DF32D424C8CA3400BCE2E3 /* PostStatusBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostStatusBadge.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 17DF328524C87D3500BCE2E3 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 17DF32C024C87D7B00BCE2E3 /* WriteFreely in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 17DF328D24C87D3500BCE2E3 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 17DF32C324C87D8D00BCE2E3 /* WriteFreely in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 17DF329524C87D3500BCE2E3 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 17DF32A024C87D3500BCE2E3 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 17120DA624E19CE2002B9F6C /* Settings */ = { + isa = PBXGroup; + children = ( + 17120DA424E19CBF002B9F6C /* SettingsView.swift */, + 17120DB124E1E19C002B9F6C /* SettingsHeaderView.swift */, + ); + path = Settings; + sourceTree = ""; + }; 1756AE7F24CB841200FD7257 /* Extensions */ = { isa = PBXGroup; children = ( 1756AE8024CB844500FD7257 /* View+Keyboard.swift */, ); path = Extensions; sourceTree = ""; }; + 1765F62C24E1924800C9EBF0 /* Preferences */ = { + isa = PBXGroup; + children = ( + 17A5389124DDED0000DEFF9A /* PreferencesView.swift */, + 17D435E724E3128F0036B539 /* PreferencesModel.swift */, + ); + path = Preferences; + sourceTree = ""; + }; + 17A5388924DDA50500DEFF9A /* Settings */ = { + isa = PBXGroup; + children = ( + 17A5388724DDA31F00DEFF9A /* MacAccountView.swift */, + 1753F6AB24E431CC00309365 /* MacPreferencesView.swift */, + ); + path = Settings; + sourceTree = ""; + }; 17DF327B24C87D3300BCE2E3 = { isa = PBXGroup; children = ( 17DF32C624C884FF00BCE2E3 /* README.md */, 17DF32C924C8855E00BCE2E3 /* LICENSE.md */, 17DF32CA24C8856C00BCE2E3 /* CHANGELOG.md */, 17DF32C724C8853700BCE2E3 /* CODE_OF_CONDUCT.md */, 17DF32C824C8854B00BCE2E3 /* CONTRIBUTING.md */, 17DF328024C87D3300BCE2E3 /* Shared */, 17DF328A24C87D3500BCE2E3 /* iOS */, 17DF329124C87D3500BCE2E3 /* macOS */, 17DF329B24C87D3500BCE2E3 /* Tests iOS */, 17DF32A624C87D3500BCE2E3 /* Tests macOS */, 17DF328924C87D3500BCE2E3 /* Products */, 17DF32C124C87D8D00BCE2E3 /* Frameworks */, ); sourceTree = ""; }; 17DF328024C87D3300BCE2E3 /* Shared */ = { isa = PBXGroup; children = ( 17DF328124C87D3300BCE2E3 /* WriteFreely_MultiPlatformApp.swift */, 17DF328324C87D3500BCE2E3 /* Assets.xcassets */, - 17DF32D024C8B75C00BCE2E3 /* Model */, + 17DF32D024C8B75C00BCE2E3 /* Account */, 1756AE7F24CB841200FD7257 /* Extensions */, 17DF32CC24C8B72300BCE2E3 /* Navigation */, - 17DF32D224C8B78D00BCE2E3 /* PostCollection */, 17DF32D124C8B78500BCE2E3 /* Post */, - 17DF32D324C8C9F600BCE2E3 /* Components */, + 17DF32D224C8B78D00BCE2E3 /* PostCollection */, + 1765F62C24E1924800C9EBF0 /* Preferences */, ); path = Shared; sourceTree = ""; }; 17DF328924C87D3500BCE2E3 /* Products */ = { isa = PBXGroup; children = ( 17DF328824C87D3500BCE2E3 /* WriteFreely-MultiPlatform.app */, 17DF329024C87D3500BCE2E3 /* WriteFreely-MultiPlatform.app */, 17DF329824C87D3500BCE2E3 /* Tests iOS.xctest */, 17DF32A324C87D3500BCE2E3 /* Tests macOS.xctest */, ); name = Products; sourceTree = ""; }; 17DF328A24C87D3500BCE2E3 /* iOS */ = { isa = PBXGroup; children = ( 17DF328B24C87D3500BCE2E3 /* Info.plist */, + 17120DA624E19CE2002B9F6C /* Settings */, ); path = iOS; sourceTree = ""; }; 17DF329124C87D3500BCE2E3 /* macOS */ = { isa = PBXGroup; children = ( 17DF329224C87D3500BCE2E3 /* Info.plist */, 17DF329324C87D3500BCE2E3 /* macOS.entitlements */, + 17A5388924DDA50500DEFF9A /* Settings */, ); path = macOS; sourceTree = ""; }; 17DF329B24C87D3500BCE2E3 /* Tests iOS */ = { isa = PBXGroup; children = ( 17DF329C24C87D3500BCE2E3 /* Tests_iOS.swift */, 17DF329E24C87D3500BCE2E3 /* Info.plist */, ); path = "Tests iOS"; sourceTree = ""; }; 17DF32A624C87D3500BCE2E3 /* Tests macOS */ = { isa = PBXGroup; children = ( 17DF32A724C87D3500BCE2E3 /* Tests_macOS.swift */, 17DF32A924C87D3500BCE2E3 /* Info.plist */, ); path = "Tests macOS"; sourceTree = ""; }; 17DF32C124C87D8D00BCE2E3 /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; 17DF32CC24C8B72300BCE2E3 /* Navigation */ = { isa = PBXGroup; children = ( 17DF328224C87D3300BCE2E3 /* ContentView.swift */, + 1765F62924E18EA200C9EBF0 /* SidebarView.swift */, ); path = Navigation; sourceTree = ""; }; - 17DF32D024C8B75C00BCE2E3 /* Model */ = { + 17DF32D024C8B75C00BCE2E3 /* Account */ = { isa = PBXGroup; children = ( + 17A5388B24DDC83F00DEFF9A /* AccountModel.swift */, + 17A5388D24DDEC7400DEFF9A /* AccountView.swift */, + 17120DA824E1B2F5002B9F6C /* AccountLogoutView.swift */, + 17120DAB24E1B99F002B9F6C /* AccountLoginView.swift */, ); - path = Model; + path = Account; sourceTree = ""; }; 17DF32D124C8B78500BCE2E3 /* Post */ = { isa = PBXGroup; children = ( 1756AE6A24CB1E4B00FD7257 /* Post.swift */, 1756AE7324CB26FA00FD7257 /* PostCell.swift */, 1756AE7624CB2EDD00FD7257 /* PostEditor.swift */, 1756AE7924CB65DF00FD7257 /* PostList.swift */, 1756AE6D24CB255B00FD7257 /* PostStore.swift */, 17DF32D424C8CA3400BCE2E3 /* PostStatusBadge.swift */, ); path = Post; sourceTree = ""; }; 17DF32D224C8B78D00BCE2E3 /* PostCollection */ = { isa = PBXGroup; children = ( 171BFDF624D49FD400888236 /* PostCollection.swift */, - 171BFDF924D4AF8300888236 /* CollectionSidebar.swift */, + 171BFDF924D4AF8300888236 /* CollectionListView.swift */, ); path = PostCollection; sourceTree = ""; }; - 17DF32D324C8C9F600BCE2E3 /* Components */ = { - isa = PBXGroup; - children = ( - ); - path = Components; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 17DF328724C87D3500BCE2E3 /* WriteFreely-MultiPlatform (iOS) */ = { isa = PBXNativeTarget; buildConfigurationList = 17DF32B224C87D3500BCE2E3 /* Build configuration list for PBXNativeTarget "WriteFreely-MultiPlatform (iOS)" */; buildPhases = ( 17DF328424C87D3500BCE2E3 /* Sources */, 17DF328524C87D3500BCE2E3 /* Frameworks */, 17DF328624C87D3500BCE2E3 /* Resources */, 17DF32C424C87E6700BCE2E3 /* ShellScript */, ); buildRules = ( ); dependencies = ( ); name = "WriteFreely-MultiPlatform (iOS)"; packageProductDependencies = ( 17DF32BF24C87D7B00BCE2E3 /* WriteFreely */, ); productName = "WriteFreely-MultiPlatform (iOS)"; productReference = 17DF328824C87D3500BCE2E3 /* WriteFreely-MultiPlatform.app */; productType = "com.apple.product-type.application"; }; 17DF328F24C87D3500BCE2E3 /* WriteFreely-MultiPlatform (macOS) */ = { isa = PBXNativeTarget; buildConfigurationList = 17DF32B524C87D3500BCE2E3 /* Build configuration list for PBXNativeTarget "WriteFreely-MultiPlatform (macOS)" */; buildPhases = ( 17DF328C24C87D3500BCE2E3 /* Sources */, 17DF328D24C87D3500BCE2E3 /* Frameworks */, 17DF328E24C87D3500BCE2E3 /* Resources */, 17DF32C524C87FDB00BCE2E3 /* ShellScript */, ); buildRules = ( ); dependencies = ( ); name = "WriteFreely-MultiPlatform (macOS)"; packageProductDependencies = ( 17DF32C224C87D8D00BCE2E3 /* WriteFreely */, ); productName = "WriteFreely-MultiPlatform (macOS)"; productReference = 17DF329024C87D3500BCE2E3 /* WriteFreely-MultiPlatform.app */; productType = "com.apple.product-type.application"; }; 17DF329724C87D3500BCE2E3 /* Tests iOS */ = { isa = PBXNativeTarget; buildConfigurationList = 17DF32B824C87D3500BCE2E3 /* Build configuration list for PBXNativeTarget "Tests iOS" */; buildPhases = ( 17DF329424C87D3500BCE2E3 /* Sources */, 17DF329524C87D3500BCE2E3 /* Frameworks */, 17DF329624C87D3500BCE2E3 /* Resources */, ); buildRules = ( ); dependencies = ( 17DF329A24C87D3500BCE2E3 /* PBXTargetDependency */, ); name = "Tests iOS"; productName = "Tests iOS"; productReference = 17DF329824C87D3500BCE2E3 /* Tests iOS.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; 17DF32A224C87D3500BCE2E3 /* Tests macOS */ = { isa = PBXNativeTarget; buildConfigurationList = 17DF32BB24C87D3500BCE2E3 /* Build configuration list for PBXNativeTarget "Tests macOS" */; buildPhases = ( 17DF329F24C87D3500BCE2E3 /* Sources */, 17DF32A024C87D3500BCE2E3 /* Frameworks */, 17DF32A124C87D3500BCE2E3 /* Resources */, ); buildRules = ( ); dependencies = ( 17DF32A524C87D3500BCE2E3 /* PBXTargetDependency */, ); name = "Tests macOS"; productName = "Tests macOS"; productReference = 17DF32A324C87D3500BCE2E3 /* Tests macOS.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 17DF327C24C87D3300BCE2E3 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1200; LastUpgradeCheck = 1200; TargetAttributes = { 17DF328724C87D3500BCE2E3 = { CreatedOnToolsVersion = 12.0; }; 17DF328F24C87D3500BCE2E3 = { CreatedOnToolsVersion = 12.0; }; 17DF329724C87D3500BCE2E3 = { CreatedOnToolsVersion = 12.0; TestTargetID = 17DF328724C87D3500BCE2E3; }; 17DF32A224C87D3500BCE2E3 = { CreatedOnToolsVersion = 12.0; TestTargetID = 17DF328F24C87D3500BCE2E3; }; }; }; buildConfigurationList = 17DF327F24C87D3300BCE2E3 /* Build configuration list for PBXProject "WriteFreely-MultiPlatform" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 17DF327B24C87D3300BCE2E3; packageReferences = ( 17DF32BE24C87D7B00BCE2E3 /* XCRemoteSwiftPackageReference "writefreely-swift" */, ); productRefGroup = 17DF328924C87D3500BCE2E3 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 17DF328724C87D3500BCE2E3 /* WriteFreely-MultiPlatform (iOS) */, 17DF328F24C87D3500BCE2E3 /* WriteFreely-MultiPlatform (macOS) */, 17DF329724C87D3500BCE2E3 /* Tests iOS */, 17DF32A224C87D3500BCE2E3 /* Tests macOS */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 17DF328624C87D3500BCE2E3 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 17DF32AE24C87D3500BCE2E3 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 17DF328E24C87D3500BCE2E3 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 17DF32AF24C87D3500BCE2E3 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 17DF329624C87D3500BCE2E3 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 17DF32A124C87D3500BCE2E3 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 17DF32C424C87E6700BCE2E3 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( ); outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "# Run SwiftLint on builds\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; 17DF32C524C87FDB00BCE2E3 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( ); outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "# Run SwiftLint on builds\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 17DF328424C87D3500BCE2E3 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 17DF32AC24C87D3500BCE2E3 /* ContentView.swift in Sources */, 1756AE8124CB844500FD7257 /* View+Keyboard.swift in Sources */, - 171BFDFA24D4AF8300888236 /* CollectionSidebar.swift in Sources */, + 17120DAC24E1B99F002B9F6C /* AccountLoginView.swift in Sources */, + 17120DA924E1B2F5002B9F6C /* AccountLogoutView.swift in Sources */, + 171BFDFA24D4AF8300888236 /* CollectionListView.swift in Sources */, + 17120DB224E1E19C002B9F6C /* SettingsHeaderView.swift in Sources */, 1756AE7724CB2EDD00FD7257 /* PostEditor.swift in Sources */, 17DF32D524C8CA3400BCE2E3 /* PostStatusBadge.swift in Sources */, + 17D435E824E3128F0036B539 /* PreferencesModel.swift in Sources */, + 1765F62A24E18EA200C9EBF0 /* SidebarView.swift in Sources */, 1756AE7A24CB65DF00FD7257 /* PostList.swift in Sources */, 171BFDF724D49FD400888236 /* PostCollection.swift in Sources */, 17DF32AA24C87D3500BCE2E3 /* WriteFreely_MultiPlatformApp.swift in Sources */, + 17120DA724E19D11002B9F6C /* SettingsView.swift in Sources */, + 17120DA224E1985C002B9F6C /* AccountModel.swift in Sources */, + 17120DA324E19A42002B9F6C /* PreferencesView.swift in Sources */, 1756AE6E24CB255B00FD7257 /* PostStore.swift in Sources */, 1756AE6B24CB1E4B00FD7257 /* Post.swift in Sources */, + 17120DA124E19839002B9F6C /* AccountView.swift in Sources */, 1756AE7424CB26FA00FD7257 /* PostCell.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 17DF328C24C87D3500BCE2E3 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 171BFDF824D49FD400888236 /* PostCollection.swift in Sources */, 17DF32AD24C87D3500BCE2E3 /* ContentView.swift in Sources */, + 1765F62B24E18EA200C9EBF0 /* SidebarView.swift in Sources */, 1756AE7824CB2EDD00FD7257 /* PostEditor.swift in Sources */, + 17D435E924E3128F0036B539 /* PreferencesModel.swift in Sources */, + 17120DAA24E1B2F5002B9F6C /* AccountLogoutView.swift in Sources */, 17DF32D624C8CA3400BCE2E3 /* PostStatusBadge.swift in Sources */, + 17120DAD24E1B99F002B9F6C /* AccountLoginView.swift in Sources */, 1756AE7B24CB65DF00FD7257 /* PostList.swift in Sources */, - 171BFDFB24D4AF8300888236 /* CollectionSidebar.swift in Sources */, + 1753F6AC24E431CC00309365 /* MacPreferencesView.swift in Sources */, + 171BFDFB24D4AF8300888236 /* CollectionListView.swift in Sources */, 17DF32AB24C87D3500BCE2E3 /* WriteFreely_MultiPlatformApp.swift in Sources */, + 17A5388C24DDC83F00DEFF9A /* AccountModel.swift in Sources */, + 17A5389324DDED0000DEFF9A /* PreferencesView.swift in Sources */, 1756AE6F24CB255B00FD7257 /* PostStore.swift in Sources */, 1756AE6C24CB1E4B00FD7257 /* Post.swift in Sources */, + 17A5388F24DDEC7400DEFF9A /* AccountView.swift in Sources */, 1756AE7524CB26FA00FD7257 /* PostCell.swift in Sources */, + 17A5388824DDA31F00DEFF9A /* MacAccountView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 17DF329424C87D3500BCE2E3 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 17DF329D24C87D3500BCE2E3 /* Tests_iOS.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 17DF329F24C87D3500BCE2E3 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 17DF32A824C87D3500BCE2E3 /* Tests_macOS.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 17DF329A24C87D3500BCE2E3 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 17DF328724C87D3500BCE2E3 /* WriteFreely-MultiPlatform (iOS) */; targetProxy = 17DF329924C87D3500BCE2E3 /* PBXContainerItemProxy */; }; 17DF32A524C87D3500BCE2E3 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 17DF328F24C87D3500BCE2E3 /* WriteFreely-MultiPlatform (macOS) */; targetProxy = 17DF32A424C87D3500BCE2E3 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 17DF32B024C87D3500BCE2E3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 17DF32B124C87D3500BCE2E3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; 17DF32B324C87D3500BCE2E3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = TPPAB4YBA6; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = iOS/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 0.0.3; PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform"; PRODUCT_NAME = "WriteFreely-MultiPlatform"; SDKROOT = iphoneos; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 17DF32B424C87D3500BCE2E3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = TPPAB4YBA6; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = iOS/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 0.0.3; PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform"; PRODUCT_NAME = "WriteFreely-MultiPlatform"; SDKROOT = iphoneos; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; 17DF32B624C87D3500BCE2E3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = TPPAB4YBA6; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = macOS/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.16; PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform"; PRODUCT_NAME = "WriteFreely-MultiPlatform"; SDKROOT = macosx; SWIFT_VERSION = 5.0; }; name = Debug; }; 17DF32B724C87D3500BCE2E3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = TPPAB4YBA6; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = macOS/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.16; PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform"; PRODUCT_NAME = "WriteFreely-MultiPlatform"; SDKROOT = macosx; SWIFT_VERSION = 5.0; }; name = Release; }; 17DF32B924C87D3500BCE2E3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = TPPAB4YBA6; INFOPLIST_FILE = "Tests iOS/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = "WriteFreely-MultiPlatform (iOS)"; }; name = Debug; }; 17DF32BA24C87D3500BCE2E3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = TPPAB4YBA6; INFOPLIST_FILE = "Tests iOS/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = "WriteFreely-MultiPlatform (iOS)"; VALIDATE_PRODUCT = YES; }; name = Release; }; 17DF32BC24C87D3500BCE2E3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = TPPAB4YBA6; INFOPLIST_FILE = "Tests macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.Tests-macOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SWIFT_VERSION = 5.0; TEST_TARGET_NAME = "WriteFreely-MultiPlatform (macOS)"; }; name = Debug; }; 17DF32BD24C87D3500BCE2E3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = TPPAB4YBA6; INFOPLIST_FILE = "Tests macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.Tests-macOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SWIFT_VERSION = 5.0; TEST_TARGET_NAME = "WriteFreely-MultiPlatform (macOS)"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 17DF327F24C87D3300BCE2E3 /* Build configuration list for PBXProject "WriteFreely-MultiPlatform" */ = { isa = XCConfigurationList; buildConfigurations = ( 17DF32B024C87D3500BCE2E3 /* Debug */, 17DF32B124C87D3500BCE2E3 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 17DF32B224C87D3500BCE2E3 /* Build configuration list for PBXNativeTarget "WriteFreely-MultiPlatform (iOS)" */ = { isa = XCConfigurationList; buildConfigurations = ( 17DF32B324C87D3500BCE2E3 /* Debug */, 17DF32B424C87D3500BCE2E3 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 17DF32B524C87D3500BCE2E3 /* Build configuration list for PBXNativeTarget "WriteFreely-MultiPlatform (macOS)" */ = { isa = XCConfigurationList; buildConfigurations = ( 17DF32B624C87D3500BCE2E3 /* Debug */, 17DF32B724C87D3500BCE2E3 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 17DF32B824C87D3500BCE2E3 /* Build configuration list for PBXNativeTarget "Tests iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 17DF32B924C87D3500BCE2E3 /* Debug */, 17DF32BA24C87D3500BCE2E3 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 17DF32BB24C87D3500BCE2E3 /* Build configuration list for PBXNativeTarget "Tests macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 17DF32BC24C87D3500BCE2E3 /* Debug */, 17DF32BD24C87D3500BCE2E3 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ 17DF32BE24C87D7B00BCE2E3 /* XCRemoteSwiftPackageReference "writefreely-swift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "git@github.com:writeas/writefreely-swift.git"; requirement = { kind = upToNextMajorVersion; minimumVersion = 0.1.1; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ 17DF32BF24C87D7B00BCE2E3 /* WriteFreely */ = { isa = XCSwiftPackageProductDependency; package = 17DF32BE24C87D7B00BCE2E3 /* XCRemoteSwiftPackageReference "writefreely-swift" */; productName = WriteFreely; }; 17DF32C224C87D8D00BCE2E3 /* WriteFreely */ = { isa = XCSwiftPackageProductDependency; package = 17DF32BE24C87D7B00BCE2E3 /* XCRemoteSwiftPackageReference "writefreely-swift" */; productName = WriteFreely; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 17DF327C24C87D3300BCE2E3 /* Project object */; } diff --git a/iOS/Info.plist b/iOS/Info.plist index efc211a..dfbdd44 100644 --- a/iOS/Info.plist +++ b/iOS/Info.plist @@ -1,50 +1,50 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion 1 LSRequiresIPhoneOS UIApplicationSceneManifest UIApplicationSupportsMultipleScenes UIApplicationSupportsIndirectInputEvents UILaunchScreen UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight diff --git a/iOS/Settings/SettingsHeaderView.swift b/iOS/Settings/SettingsHeaderView.swift new file mode 100644 index 0000000..75121d7 --- /dev/null +++ b/iOS/Settings/SettingsHeaderView.swift @@ -0,0 +1,26 @@ +import SwiftUI + +struct SettingsHeaderView: View { + @Binding var isPresented: Bool + + var body: some View { + HStack { + Text("Settings") + .font(.largeTitle) + .fontWeight(.bold) + Spacer() + Button(action: { + isPresented = false + }, label: { + Image(systemName: "xmark.circle") + }) + } + .padding() + } +} + +struct SettingsHeaderView_Previews: PreviewProvider { + static var previews: some View { + SettingsHeaderView(isPresented: .constant(true)) + } +} diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift new file mode 100644 index 0000000..5bf73a2 --- /dev/null +++ b/iOS/Settings/SettingsView.swift @@ -0,0 +1,29 @@ +import SwiftUI + +struct SettingsView: View { + @EnvironmentObject var preferences: PreferencesModel + @EnvironmentObject var account: AccountModel + + @Binding var isPresented: Bool + + var body: some View { + VStack { + SettingsHeaderView(isPresented: $isPresented) + Form { + Section(header: Text("Login Details")) { + AccountView(account: account) + } + Section(header: Text("Appearance")) { + PreferencesView(preferences: preferences) + } + } + } +// .preferredColorScheme(preferences.selectedColorScheme) // See PreferencesModel for info. + } +} + +struct SettingsView_Previews: PreviewProvider { + static var previews: some View { + SettingsView(isPresented: .constant(true)) + } +} diff --git a/macOS/Settings/MacAccountView.swift b/macOS/Settings/MacAccountView.swift new file mode 100644 index 0000000..b37b6da --- /dev/null +++ b/macOS/Settings/MacAccountView.swift @@ -0,0 +1,19 @@ +import SwiftUI + +struct MacAccountView: View { + @ObservedObject var account: AccountModel + + var body: some View { + Form { + Section(header: Text("Login Details")) { + AccountView(account: account) + } + } + } +} + +struct MacAccountView_Previews: PreviewProvider { + static var previews: some View { + MacAccountView(account: AccountModel()) + } +} diff --git a/macOS/Settings/MacPreferencesView.swift b/macOS/Settings/MacPreferencesView.swift new file mode 100644 index 0000000..85fa829 --- /dev/null +++ b/macOS/Settings/MacPreferencesView.swift @@ -0,0 +1,18 @@ +import SwiftUI + +struct MacPreferencesView: View { + @ObservedObject var preferences: PreferencesModel + + var body: some View { + VStack { + PreferencesView(preferences: preferences) + Spacer() + } + } +} + +struct MacPreferencesView_Previews: PreviewProvider { + static var previews: some View { + MacPreferencesView(preferences: PreferencesModel()) + } +} diff --git a/macOS/Settings/SettingsView.swift b/macOS/Settings/SettingsView.swift new file mode 100644 index 0000000..16c7df8 --- /dev/null +++ b/macOS/Settings/SettingsView.swift @@ -0,0 +1,44 @@ +import SwiftUI + +struct MacAccountView: View { + @ObservedObject var preferences: PreferencesModel + @ObservedObject var account: AccountModel + + @State var selectedView = 0 + + var body: some View { + TabView(selection: $selectedView) { + Form { + Section(header: Text("Login Details")) { + AccountView(account: account) + } + } + .tabItem { + Image(systemName: "person.crop.circle") + Text("Account") + } + .tag(0) + VStack { + PreferencesView(preferences: preferences) + Spacer() + } + .tabItem { + Image(systemName: "gear") + Text("Preferences") + } + .tag(1) + } + } +} + +struct SettingsView_AccountTabPreviews: PreviewProvider { + static var previews: some View { + MacAccountView(preferences: PreferencesModel(), account: AccountModel(), selectedView: 0) + } +} + +struct SettingsView_PreferencesTabPreviews: PreviewProvider { + static var previews: some View { + MacAccountView(preferences: PreferencesModel(), account: AccountModel(), selectedView: 1) + } +}