Source code for the WriteFreely SwiftUI app for iOS, iPadOS, and macOS
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

213 rader
7.7 KiB

  1. import SwiftUI
  2. #if os(macOS)
  3. import Sparkle
  4. #endif
  5. @main
  6. struct CheckForDebugModifier {
  7. static func main() {
  8. #if os(macOS)
  9. if NSEvent.modifierFlags.contains(.shift) {
  10. // Clear the launch-to-last-draft values to load a new draft.
  11. UserDefaults.shared.setValue(false, forKey: WFDefaults.showAllPostsFlag)
  12. UserDefaults.shared.setValue(nil, forKey: WFDefaults.selectedCollectionURL)
  13. UserDefaults.shared.setValue(nil, forKey: WFDefaults.lastDraftURL)
  14. } else {
  15. // No-op
  16. }
  17. #endif
  18. WriteFreely_MultiPlatformApp.main()
  19. }
  20. }
  21. struct WriteFreely_MultiPlatformApp: App {
  22. @StateObject private var model = WriteFreelyModel.shared
  23. private let logger = Logging(for: String(describing: WriteFreely_MultiPlatformApp.self))
  24. #if os(macOS)
  25. @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
  26. @StateObject var updaterViewModel = MacUpdatesViewModel()
  27. @State private var selectedTab = 0
  28. #endif
  29. @State private var didCrash = UserDefaults.shared.bool(forKey: WFDefaults.didHaveFatalError)
  30. var body: some Scene {
  31. WindowGroup {
  32. ContentView()
  33. .onAppear(perform: {
  34. if model.editor.showAllPostsFlag {
  35. DispatchQueue.main.async {
  36. self.model.selectedCollection = nil
  37. self.model.showAllPosts = true
  38. showLastDraftOrCreateNewLocalPost()
  39. }
  40. } else {
  41. DispatchQueue.main.async {
  42. self.model.selectedCollection = model.editor.fetchSelectedCollectionFromAppStorage()
  43. self.model.showAllPosts = false
  44. showLastDraftOrCreateNewLocalPost()
  45. }
  46. }
  47. })
  48. .alert(isPresented: $didCrash) {
  49. var helpMsg = "Alert the humans by sharing what happened on the help forum."
  50. if let errorMsg = UserDefaults.shared.object(forKey: WFDefaults.fatalErrorDescription) as? String {
  51. helpMsg.append("\n\n\(errorMsg)")
  52. }
  53. return Alert(
  54. title: Text("Crash Detected"),
  55. message: Text(helpMsg),
  56. primaryButton: .default(
  57. Text("Let us know"), action: didPressCrashAlertButton
  58. ),
  59. secondaryButton: .cancel(
  60. Text("Dismiss"),
  61. action: resetCrashFlags
  62. )
  63. )
  64. }
  65. .onAppear {
  66. if #available(iOS 15, *) {
  67. if didCrash { generateCrashLogPost() }
  68. }
  69. }
  70. .withErrorHandling()
  71. .environmentObject(model)
  72. .environment(\.managedObjectContext, LocalStorageManager.standard.container.viewContext)
  73. // .preferredColorScheme(preferences.selectedColorScheme) // See PreferencesModel for info.
  74. }
  75. .commands {
  76. #if os(macOS)
  77. CommandGroup(after: .appInfo) {
  78. CheckForUpdatesView(updaterViewModel: updaterViewModel)
  79. }
  80. #endif
  81. CommandGroup(replacing: .newItem, addition: {
  82. Button("New Post") {
  83. createNewLocalPost()
  84. }
  85. .keyboardShortcut("n", modifiers: [.command])
  86. })
  87. CommandGroup(after: .newItem) {
  88. Button("Refresh Posts") {
  89. DispatchQueue.main.async {
  90. model.fetchUserCollections()
  91. model.fetchUserPosts()
  92. }
  93. }
  94. .disabled(!model.account.isLoggedIn)
  95. .keyboardShortcut("r", modifiers: [.command])
  96. }
  97. SidebarCommands()
  98. #if os(macOS)
  99. PostCommands(model: model)
  100. HelpCommands(model: model)
  101. #endif
  102. ToolbarCommands()
  103. TextEditingCommands()
  104. }
  105. #if os(macOS)
  106. Settings {
  107. TabView(selection: $selectedTab) {
  108. MacAccountView()
  109. .environmentObject(model)
  110. .tabItem {
  111. Image(systemName: "person.crop.circle")
  112. Text("Account")
  113. }
  114. .tag(0)
  115. MacPreferencesView(preferences: model.preferences)
  116. .tabItem {
  117. Image(systemName: "gear")
  118. Text("Preferences")
  119. }
  120. .tag(1)
  121. MacUpdatesView(updaterViewModel: updaterViewModel)
  122. .tabItem {
  123. Image(systemName: "arrow.down.circle")
  124. Text("Updates")
  125. }
  126. .tag(2)
  127. }
  128. .environmentObject(model)
  129. .withErrorHandling()
  130. .frame(minWidth: 500, maxWidth: 500, minHeight: 200)
  131. .padding()
  132. // .preferredColorScheme(preferences.selectedColorScheme) // See PreferencesModel for info.
  133. }
  134. #endif
  135. }
  136. private func showLastDraftOrCreateNewLocalPost() {
  137. if model.editor.lastDraftURL != nil {
  138. DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
  139. self.model.selectedPost = model.editor.fetchLastDraftFromAppStorage()
  140. }
  141. } else {
  142. createNewLocalPost()
  143. }
  144. }
  145. private func createNewLocalPost() {
  146. withAnimation {
  147. // Un-set the currently selected post
  148. self.model.selectedPost = nil
  149. }
  150. // Create the new-post managed object
  151. let managedPost = model.editor.generateNewLocalPost(withFont: model.preferences.font)
  152. withAnimation {
  153. // Set it as the selectedPost
  154. DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
  155. self.model.selectedPost = managedPost
  156. }
  157. }
  158. }
  159. @available(iOS 15, *)
  160. private func generateCrashLogPost() {
  161. logger.log("Generating local log post...")
  162. DispatchQueue.main.asyncAfter(deadline: .now()) {
  163. // Unset selected post and collection and navigate to local drafts.
  164. self.model.selectedPost = nil
  165. self.model.selectedCollection = nil
  166. self.model.showAllPosts = false
  167. // Create the new log post.
  168. let newLogPost = model.editor.generateNewLocalPost(withFont: 2)
  169. newLogPost.title = "Logs For Support"
  170. var postBody: [String] = [
  171. "WriteFreely-Multiplatform v\(Bundle.main.appMarketingVersion) (\(Bundle.main.appBuildVersion))",
  172. "Generated \(Date())",
  173. ""
  174. ]
  175. postBody.append(contentsOf: logger.fetchLogs())
  176. newLogPost.body = postBody.joined(separator: "\n")
  177. self.model.selectedPost = newLogPost
  178. }
  179. logger.log("Generated local log post.")
  180. }
  181. private func resetCrashFlags() {
  182. UserDefaults.shared.set(false, forKey: WFDefaults.didHaveFatalError)
  183. UserDefaults.shared.removeObject(forKey: WFDefaults.fatalErrorDescription)
  184. }
  185. private func didPressCrashAlertButton() {
  186. resetCrashFlags()
  187. #if os(macOS)
  188. NSWorkspace().open(model.helpURL)
  189. #else
  190. UIApplication.shared.open(model.helpURL)
  191. #endif
  192. }
  193. }