diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/AppIconExtension@2x.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/AppIconExtension@2x.png deleted file mode 100644 index 75f2008..0000000 Binary files a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/AppIconExtension@2x.png and /dev/null differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/AppIconExtension@3x.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/AppIconExtension@3x.png deleted file mode 100644 index 1fd381e..0000000 Binary files a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/AppIconExtension@3x.png and /dev/null differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/Contents.json b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/Contents.json index 4dac38e..855b1ac 100644 --- a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/Contents.json +++ b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/Contents.json @@ -1,93 +1,109 @@ { "images" : [ { + "filename" : "notification-icon@2x.png", "idiom" : "iphone", "scale" : "2x", "size" : "20x20" }, { + "filename" : "notification-icon@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "20x20" }, { + "filename" : "icon-small@2x.png", "idiom" : "iphone", "scale" : "2x", "size" : "29x29" }, { + "filename" : "icon-small@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "29x29" }, { + "filename" : "icon-40@2x.png", "idiom" : "iphone", "scale" : "2x", "size" : "40x40" }, { + "filename" : "icon-40@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "40x40" }, { - "filename" : "AppIconExtension@2x.png", + "filename" : "icon-60@2x.png", "idiom" : "iphone", "scale" : "2x", "size" : "60x60" }, { - "filename" : "AppIconExtension@3x.png", + "filename" : "icon-60@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "60x60" }, { + "filename" : "notification-icon~ipad.png", "idiom" : "ipad", "scale" : "1x", "size" : "20x20" }, { + "filename" : "notification-icon~ipad@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "20x20" }, { + "filename" : "icon-small.png", "idiom" : "ipad", "scale" : "1x", "size" : "29x29" }, { + "filename" : "icon-small@2x-1.png", "idiom" : "ipad", "scale" : "2x", "size" : "29x29" }, { + "filename" : "icon-40.png", "idiom" : "ipad", "scale" : "1x", "size" : "40x40" }, { + "filename" : "icon-40@2x-1.png", "idiom" : "ipad", "scale" : "2x", "size" : "40x40" }, { + "filename" : "icon-76.png", "idiom" : "ipad", "scale" : "1x", "size" : "76x76" }, { + "filename" : "icon-76@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "76x76" }, { + "filename" : "icon-83.5@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "83.5x83.5" }, { + "filename" : "ios-marketing.png", "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-40.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-40.png new file mode 100644 index 0000000..662e8f4 Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-40.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-40@2x-1.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-40@2x-1.png new file mode 100644 index 0000000..19926ea Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-40@2x-1.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-40@2x.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-40@2x.png new file mode 100644 index 0000000..19926ea Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-40@2x.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-40@3x.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-40@3x.png new file mode 100644 index 0000000..fddce80 Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-40@3x.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-60@2x.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-60@2x.png new file mode 100644 index 0000000..fddce80 Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-60@2x.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-60@3x.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-60@3x.png new file mode 100644 index 0000000..8b73775 Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-60@3x.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-76.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-76.png new file mode 100644 index 0000000..795b55c Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-76.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-76@2x.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-76@2x.png new file mode 100644 index 0000000..f64902d Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-76@2x.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-83.5@2x.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-83.5@2x.png new file mode 100644 index 0000000..c07d304 Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-83.5@2x.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-small.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-small.png new file mode 100644 index 0000000..ebc122e Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-small.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-small@2x-1.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-small@2x-1.png new file mode 100644 index 0000000..f30d38f Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-small@2x-1.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-small@2x.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-small@2x.png new file mode 100644 index 0000000..f30d38f Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-small@2x.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-small@3x.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-small@3x.png new file mode 100644 index 0000000..21db5a4 Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/icon-small@3x.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/ios-marketing.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/ios-marketing.png new file mode 100644 index 0000000..dbe9d5d Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/ios-marketing.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/notification-icon@2x.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/notification-icon@2x.png new file mode 100644 index 0000000..662e8f4 Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/notification-icon@2x.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/notification-icon@3x.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/notification-icon@3x.png new file mode 100644 index 0000000..6855f65 Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/notification-icon@3x.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/notification-icon~ipad.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/notification-icon~ipad.png new file mode 100644 index 0000000..8b506c9 Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/notification-icon~ipad.png differ diff --git a/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/notification-icon~ipad@2x.png b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/notification-icon~ipad@2x.png new file mode 100644 index 0000000..662e8f4 Binary files /dev/null and b/ActionExtension-iOS/Media.xcassets/AppIconExtension.appiconset/notification-icon~ipad@2x.png differ diff --git a/Shared/Extensions/WriteFreelyModel+APIHandlers.swift b/Shared/Extensions/WriteFreelyModel+APIHandlers.swift index 9a7447e..804dd41 100644 --- a/Shared/Extensions/WriteFreelyModel+APIHandlers.swift +++ b/Shared/Extensions/WriteFreelyModel+APIHandlers.swift @@ -147,17 +147,8 @@ extension WriteFreelyModel { } else { DispatchQueue.main.async { let managedPost = WFAPost(context: LocalStorageManager.standard.container.viewContext) - managedPost.postId = fetchedPost.postId - managedPost.slug = fetchedPost.slug - managedPost.appearance = fetchedPost.appearance - managedPost.language = fetchedPost.language - managedPost.rtl = fetchedPost.rtl ?? false - managedPost.createdDate = fetchedPost.createdDate - managedPost.updatedDate = fetchedPost.updatedDate - managedPost.title = fetchedPost.title ?? "" - managedPost.body = fetchedPost.body + self.importData(from: fetchedPost, into: managedPost) managedPost.collectionAlias = fetchedPost.collectionAlias - managedPost.status = PostStatus.published.rawValue managedPost.wasDeletedFromServer = false } } @@ -193,16 +184,7 @@ extension WriteFreelyModel { let fetchedPost = try result.get() // If this is an updated post, check it against postToUpdate. if let updatingPost = self.postToUpdate { - updatingPost.appearance = fetchedPost.appearance - updatingPost.body = fetchedPost.body - updatingPost.createdDate = fetchedPost.createdDate - updatingPost.language = fetchedPost.language - updatingPost.postId = fetchedPost.postId - updatingPost.rtl = fetchedPost.rtl ?? false - updatingPost.slug = fetchedPost.slug - updatingPost.status = PostStatus.published.rawValue - updatingPost.title = fetchedPost.title ?? "" - updatingPost.updatedDate = fetchedPost.updatedDate + importData(from: fetchedPost, into: updatingPost) DispatchQueue.main.async { LocalStorageManager.standard.saveContext() } @@ -224,16 +206,7 @@ extension WriteFreelyModel { do { let cachedPostsResults = try LocalStorageManager.standard.container.viewContext.fetch(request) guard let cachedPost = cachedPostsResults.first else { return } - cachedPost.appearance = fetchedPost.appearance - cachedPost.body = fetchedPost.body - cachedPost.createdDate = fetchedPost.createdDate - cachedPost.language = fetchedPost.language - cachedPost.postId = fetchedPost.postId - cachedPost.rtl = fetchedPost.rtl ?? false - cachedPost.slug = fetchedPost.slug - cachedPost.status = PostStatus.published.rawValue - cachedPost.title = fetchedPost.title ?? "" - cachedPost.updatedDate = fetchedPost.updatedDate + importData(from: fetchedPost, into: cachedPost) DispatchQueue.main.async { LocalStorageManager.standard.saveContext() } @@ -258,16 +231,7 @@ extension WriteFreelyModel { do { let fetchedPost = try result.get() guard let cachedPost = self.selectedPost else { return } - cachedPost.appearance = fetchedPost.appearance - cachedPost.body = fetchedPost.body - cachedPost.createdDate = fetchedPost.createdDate - cachedPost.language = fetchedPost.language - cachedPost.postId = fetchedPost.postId - cachedPost.rtl = fetchedPost.rtl ?? false - cachedPost.slug = fetchedPost.slug - cachedPost.status = PostStatus.published.rawValue - cachedPost.title = fetchedPost.title ?? "" - cachedPost.updatedDate = fetchedPost.updatedDate + importData(from: fetchedPost, into: cachedPost) cachedPost.hasNewerRemoteCopy = false DispatchQueue.main.async { LocalStorageManager.standard.saveContext() @@ -298,4 +262,17 @@ extension WriteFreelyModel { print(error) } } + + private func importData(from fetchedPost: WFPost, into cachedPost: WFAPost) { + cachedPost.appearance = fetchedPost.appearance + cachedPost.body = fetchedPost.body + cachedPost.createdDate = fetchedPost.createdDate + cachedPost.language = fetchedPost.language + cachedPost.postId = fetchedPost.postId + cachedPost.rtl = fetchedPost.rtl ?? false + cachedPost.slug = fetchedPost.slug + cachedPost.status = PostStatus.published.rawValue + cachedPost.title = fetchedPost.title ?? "" + cachedPost.updatedDate = fetchedPost.updatedDate + } } diff --git a/Shared/LocalStorageManager.swift b/Shared/LocalStorageManager.swift index 63bdd88..b644faf 100644 --- a/Shared/LocalStorageManager.swift +++ b/Shared/LocalStorageManager.swift @@ -59,7 +59,7 @@ private extension LocalStorageManager { container.persistentStoreDescriptions.first!.url = sharedStoreURL } - container.loadPersistentStores { description, error in + container.loadPersistentStores { _, error in if let error = error { fatalError("Core Data store failed to load with error: \(error)") } diff --git a/Shared/WriteFreely_MultiPlatformApp.swift b/Shared/WriteFreely_MultiPlatformApp.swift index 0ea2760..4378ada 100644 --- a/Shared/WriteFreely_MultiPlatformApp.swift +++ b/Shared/WriteFreely_MultiPlatformApp.swift @@ -25,8 +25,8 @@ struct WriteFreely_MultiPlatformApp: App { @StateObject private var model = WriteFreelyModel.shared #if os(macOS) - // swiftlint:disable:next weak_delegate @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + @StateObject var updaterViewModel = MacUpdatesViewModel() @State private var selectedTab = 0 #endif @@ -47,13 +47,6 @@ struct WriteFreely_MultiPlatformApp: App { showLastDraftOrCreateNewLocalPost() } } -// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { -// if model.editor.lastDraftURL != nil { -// self.model.selectedPost = model.editor.fetchLastDraftFromAppStorage() -// } else { -// createNewLocalPost() -// } -// } }) .environmentObject(model) .environment(\.managedObjectContext, LocalStorageManager.standard.container.viewContext) @@ -61,11 +54,9 @@ struct WriteFreely_MultiPlatformApp: App { } .commands { #if os(macOS) - CommandGroup(after: .appInfo, addition: { - Button("Check For Updates") { - SUUpdater.shared()?.checkForUpdates(self) - } - }) + CommandGroup(after: .appInfo) { + CheckForUpdatesView(updaterViewModel: updaterViewModel) + } #endif CommandGroup(replacing: .newItem, addition: { Button("New Post") { @@ -116,7 +107,7 @@ struct WriteFreely_MultiPlatformApp: App { Text("Preferences") } .tag(1) - MacUpdatesView() + MacUpdatesView(updaterViewModel: updaterViewModel) .tabItem { Image(systemName: "arrow.down.circle") Text("Updates") diff --git a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj index a28dc78..45555ef 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj +++ b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj @@ -20,8 +20,6 @@ 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 */; }; - 1714DD65260BAC14000C0DFF /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 1714DD64260BAC14000C0DFF /* WriteFreely */; }; - 1714DD6B260BAC2C000C0DFF /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 1714DD6A260BAC2C000C0DFF /* WriteFreely */; }; 171BFDFA24D4AF8300888236 /* CollectionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF924D4AF8300888236 /* CollectionListView.swift */; }; 171BFDFB24D4AF8300888236 /* CollectionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF924D4AF8300888236 /* CollectionListView.swift */; }; 171DC677272C7D0B002B9B8A /* UserDefaults+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171DC676272C7D0B002B9B8A /* UserDefaults+Extensions.swift */; }; @@ -35,7 +33,6 @@ 172E10152735C2BD00061372 /* UIHostingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172E10142735C2BD00061372 /* UIHostingView.swift */; }; 172E10172735C2DF00061372 /* EnvironmentValues+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172E10162735C2DF00061372 /* EnvironmentValues+Extensions.swift */; }; 172E10192735C3DB00061372 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172E10182735C3DB00061372 /* ContentView.swift */; }; - 172E101B2735C54400061372 /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 172E101A2735C54400061372 /* WriteFreely */; }; 172E101C2735C57400061372 /* LocalStorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756DBB924FED45500207AB8 /* LocalStorageManager.swift */; }; 172E101D2735C5AB00061372 /* LocalStorageModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1756DBB524FED3A400207AB8 /* LocalStorageModel.xcdatamodeld */; }; 172E101E2735C62F00061372 /* PostStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17C42E612507D8E600072984 /* PostStatus.swift */; }; @@ -78,7 +75,9 @@ 17836C15273F0FBB0047AF61 /* Hack-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 17D4F3A42514F1E900517CE6 /* Hack-Regular.ttf */; }; 17836C16273F0FBB0047AF61 /* LoraGX.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 17D4F36B2514EE2F00517CE6 /* LoraGX.ttf */; }; 17836C17273F0FBB0047AF61 /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 17D4F39D2514F0E500517CE6 /* OpenSans-Regular.ttf */; }; - 17A4FEDA25924AF70037E96B /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 17A4FED925924AF70037E96B /* Sparkle */; }; + 1784D2ED27946D880033E72E /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 1784D2EC27946D880033E72E /* WriteFreely */; }; + 1784D2EF27946D9A0033E72E /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 1784D2EE27946D9A0033E72E /* WriteFreely */; }; + 1784D2F127946DA10033E72E /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 1784D2F027946DA10033E72E /* WriteFreely */; }; 17A4FEED25927E730037E96B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A4FEEC25927E730037E96B /* AppDelegate.swift */; }; 17A5388824DDA31F00DEFF9A /* MacAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388724DDA31F00DEFF9A /* MacAccountView.swift */; }; 17A5388C24DDC83F00DEFF9A /* AccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388B24DDC83F00DEFF9A /* AccountModel.swift */; }; @@ -108,6 +107,8 @@ 17C42E71250AAFD500072984 /* NSManagedObjectContext+ExecuteAndMergeChanges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17C42E6F250AA12200072984 /* NSManagedObjectContext+ExecuteAndMergeChanges.swift */; }; 17D435E824E3128F0036B539 /* PreferencesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D435E724E3128F0036B539 /* PreferencesModel.swift */; }; 17D435E924E3128F0036B539 /* PreferencesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D435E724E3128F0036B539 /* PreferencesModel.swift */; }; + 17D4926527947B4D0035BD7E /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 17D4926427947B4D0035BD7E /* Sparkle */; }; + 17D4926727947D780035BD7E /* MacUpdatesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D4926627947D780035BD7E /* MacUpdatesViewModel.swift */; }; 17D4F36C2514EE2F00517CE6 /* LoraGX.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 17D4F36B2514EE2F00517CE6 /* LoraGX.ttf */; }; 17D4F36D2514EE2F00517CE6 /* LoraGX.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 17D4F36B2514EE2F00517CE6 /* LoraGX.ttf */; }; 17D4F39E2514F0E500517CE6 /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 17D4F39D2514F0E500517CE6 /* OpenSans-Regular.ttf */; }; @@ -234,6 +235,7 @@ 17C42E642509237800072984 /* PostListFilteredView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostListFilteredView.swift; sourceTree = ""; }; 17C42E6F250AA12200072984 /* NSManagedObjectContext+ExecuteAndMergeChanges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+ExecuteAndMergeChanges.swift"; sourceTree = ""; }; 17D435E724E3128F0036B539 /* PreferencesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesModel.swift; sourceTree = ""; }; + 17D4926627947D780035BD7E /* MacUpdatesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacUpdatesViewModel.swift; sourceTree = ""; }; 17D4F36B2514EE2F00517CE6 /* LoraGX.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = LoraGX.ttf; sourceTree = ""; }; 17D4F39D2514F0E500517CE6 /* OpenSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "OpenSans-Regular.ttf"; sourceTree = ""; }; 17D4F3A42514F1E900517CE6 /* Hack-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Hack-Regular.ttf"; sourceTree = ""; }; @@ -269,7 +271,7 @@ buildActionMask = 2147483647; files = ( 172E10012735B83E00061372 /* UniformTypeIdentifiers.framework in Frameworks */, - 172E101B2735C54400061372 /* WriteFreely in Frameworks */, + 1784D2F127946DA10033E72E /* WriteFreely in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -277,7 +279,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1714DD65260BAC14000C0DFF /* WriteFreely in Frameworks */, + 1784D2ED27946D880033E72E /* WriteFreely in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -285,8 +287,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 17A4FEDA25924AF70037E96B /* Sparkle in Frameworks */, - 1714DD6B260BAC2C000C0DFF /* WriteFreely in Frameworks */, + 1784D2EF27946D9A0033E72E /* WriteFreely in Frameworks */, + 17D4926527947B4D0035BD7E /* Sparkle in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -400,6 +402,7 @@ 17A5388724DDA31F00DEFF9A /* MacAccountView.swift */, 1753F6AB24E431CC00309365 /* MacPreferencesView.swift */, 172C492D2593981900E20ADF /* MacUpdatesView.swift */, + 17D4926627947D780035BD7E /* MacUpdatesViewModel.swift */, ); path = Settings; sourceTree = ""; @@ -619,7 +622,7 @@ ); name = "ActionExtension-iOS"; packageProductDependencies = ( - 172E101A2735C54400061372 /* WriteFreely */, + 1784D2F027946DA10033E72E /* WriteFreely */, ); productName = "ActionExtension-iOS"; productReference = 172E0FFF2735B83E00061372 /* ActionExtension-iOS.appex */; @@ -642,7 +645,7 @@ ); name = "WriteFreely-MultiPlatform (iOS)"; packageProductDependencies = ( - 1714DD64260BAC14000C0DFF /* WriteFreely */, + 1784D2EC27946D880033E72E /* WriteFreely */, ); productName = "WriteFreely-MultiPlatform (iOS)"; productReference = 17DF328824C87D3500BCE2E3 /* WriteFreely-MultiPlatform.app */; @@ -663,8 +666,8 @@ ); name = "WriteFreely-MultiPlatform (macOS)"; packageProductDependencies = ( - 17A4FED925924AF70037E96B /* Sparkle */, - 1714DD6A260BAC2C000C0DFF /* WriteFreely */, + 1784D2EE27946D9A0033E72E /* WriteFreely */, + 17D4926427947B4D0035BD7E /* Sparkle */, ); productName = "WriteFreely-MultiPlatform (macOS)"; productReference = 17DF329024C87D3500BCE2E3 /* WriteFreely for Mac.app */; @@ -713,7 +716,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1310; - LastUpgradeCheck = 1200; + LastUpgradeCheck = 1320; TargetAttributes = { 172E0FFE2735B83E00061372 = { CreatedOnToolsVersion = 13.1; @@ -744,8 +747,8 @@ ); mainGroup = 17DF327B24C87D3300BCE2E3; packageReferences = ( - 17A4FED825924AF70037E96B /* XCRemoteSwiftPackageReference "Sparkle" */, - 1714DD63260BAC14000C0DFF /* XCRemoteSwiftPackageReference "writefreely-swift" */, + 1784D2EB27946D880033E72E /* XCRemoteSwiftPackageReference "writefreely-swift" */, + 17D4926327947B4D0035BD7E /* XCRemoteSwiftPackageReference "Sparkle" */, ); productRefGroup = 17DF328924C87D3500BCE2E3 /* Products */; projectDirPath = ""; @@ -942,6 +945,7 @@ 17480CA6251272EE00EB7765 /* Bundle+AppVersion.swift in Sources */, 17C42E662509237800072984 /* PostListFilteredView.swift in Sources */, 17120DAD24E1B99F002B9F6C /* AccountLoginView.swift in Sources */, + 17D4926727947D780035BD7E /* MacUpdatesViewModel.swift in Sources */, 17466626256C0D0600629997 /* MacEditorTextView.swift in Sources */, 170A7EC226F5186A00F1CBD4 /* CollectionListModel.swift in Sources */, 17E5DF8A2543610700DCDC9B /* PostTextEditingView.swift in Sources */, @@ -1243,6 +1247,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 620; @@ -1269,6 +1274,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 620; @@ -1438,43 +1444,43 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 1714DD63260BAC14000C0DFF /* XCRemoteSwiftPackageReference "writefreely-swift" */ = { + 1784D2EB27946D880033E72E /* XCRemoteSwiftPackageReference "writefreely-swift" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "git@github.com:writefreely/writefreely-swift.git"; + repositoryURL = "https://github.com/writefreely/writefreely-swift"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 0.3.2; + minimumVersion = 0.3.4; }; }; - 17A4FED825924AF70037E96B /* XCRemoteSwiftPackageReference "Sparkle" */ = { + 17D4926327947B4D0035BD7E /* XCRemoteSwiftPackageReference "Sparkle" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/writefreely/Sparkle"; + repositoryURL = "https://github.com/sparkle-project/Sparkle"; requirement = { - branch = master; - kind = branch; + kind = upToNextMinorVersion; + minimumVersion = 2.0.0; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 1714DD64260BAC14000C0DFF /* WriteFreely */ = { + 1784D2EC27946D880033E72E /* WriteFreely */ = { isa = XCSwiftPackageProductDependency; - package = 1714DD63260BAC14000C0DFF /* XCRemoteSwiftPackageReference "writefreely-swift" */; + package = 1784D2EB27946D880033E72E /* XCRemoteSwiftPackageReference "writefreely-swift" */; productName = WriteFreely; }; - 1714DD6A260BAC2C000C0DFF /* WriteFreely */ = { + 1784D2EE27946D9A0033E72E /* WriteFreely */ = { isa = XCSwiftPackageProductDependency; - package = 1714DD63260BAC14000C0DFF /* XCRemoteSwiftPackageReference "writefreely-swift" */; + package = 1784D2EB27946D880033E72E /* XCRemoteSwiftPackageReference "writefreely-swift" */; productName = WriteFreely; }; - 172E101A2735C54400061372 /* WriteFreely */ = { + 1784D2F027946DA10033E72E /* WriteFreely */ = { isa = XCSwiftPackageProductDependency; - package = 1714DD63260BAC14000C0DFF /* XCRemoteSwiftPackageReference "writefreely-swift" */; + package = 1784D2EB27946D880033E72E /* XCRemoteSwiftPackageReference "writefreely-swift" */; productName = WriteFreely; }; - 17A4FED925924AF70037E96B /* Sparkle */ = { + 17D4926427947B4D0035BD7E /* Sparkle */ = { isa = XCSwiftPackageProductDependency; - package = 17A4FED825924AF70037E96B /* XCRemoteSwiftPackageReference "Sparkle" */; + package = 17D4926327947B4D0035BD7E /* XCRemoteSwiftPackageReference "Sparkle" */; productName = Sparkle; }; /* End XCSwiftPackageProductDependency section */ diff --git a/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist b/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist index 155f2da..33a3444 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,17 +7,17 @@ ActionExtension-iOS.xcscheme_^#shared#^_ orderHint - 0 + 1 WriteFreely-MultiPlatform (iOS).xcscheme_^#shared#^_ orderHint - 1 + 2 WriteFreely-MultiPlatform (macOS).xcscheme_^#shared#^_ orderHint - 2 + 0 diff --git a/macOS/AppDelegate.swift b/macOS/AppDelegate.swift index e002e64..410ffe9 100644 --- a/macOS/AppDelegate.swift +++ b/macOS/AppDelegate.swift @@ -3,26 +3,6 @@ import Sparkle class AppDelegate: NSObject, NSApplicationDelegate { - func applicationWillFinishLaunching(_ notification: Notification) { - // Check UserDefaults for values; if the key doesn't exist (e.g., if MacUpdatesView hasn't ever been shown), - // bool(forKey:) returns false, so set SUUpdater.shared() appropriately. - let automaticallyChecksForUpdates = UserDefaults.shared.bool(forKey: WFDefaults.automaticallyChecksForUpdates) - let subscribeToBetaUpdates = UserDefaults.shared.bool(forKey: WFDefaults.subscribeToBetaUpdates) - - // Set Sparkle properties. - SUUpdater.shared()?.automaticallyChecksForUpdates = automaticallyChecksForUpdates - if subscribeToBetaUpdates { - SUUpdater.shared()?.feedURL = URL(string: AppcastFeedUrl.beta.rawValue) - } else { - SUUpdater.shared()?.feedURL = URL(string: AppcastFeedUrl.release.rawValue) - } - - // If enabled, check for updates. - if automaticallyChecksForUpdates { - SUUpdater.shared()?.checkForUpdatesInBackground() - } - } - // MARK: - Window handling when miniaturized into app icon on the Dock // Credit to Henry Cooper (pillboxer) on GitHub: // https://github.com/tact/beta-bugs/issues/31#issuecomment-855914705 @@ -30,7 +10,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { // If the window is currently minimized into the Dock, de-miniaturize it (note that if it's minimized // and the user uses OPT+TAB to switch to it, it will be de-miniaturized and brought to the foreground). func applicationDidBecomeActive(_ notification: Notification) { - print("💬 Fired:", #function) if let window = NSApp.windows.first { window.deminiaturize(nil) } @@ -39,7 +18,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { // If we're miniaturizing the window, deactivate it as well by activating Finder.app (note that // this will bring any Finder windows that are behind other apps to the foreground). func applicationDidChangeOcclusionState(_ notification: Notification) { - print("💬 Fired:", #function) if let window = NSApp.windows.first, window.isMiniaturized { NSWorkspace.shared.runningApplications.first(where: { $0.activationPolicy == .regular @@ -49,7 +27,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { lazy var windows = NSWindow() func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { - print("💬 Fired:", #function) if !flag { for window in sender.windows { window.makeKeyAndOrderFront(self) diff --git a/macOS/PostEditor/MacEditorTextView.swift b/macOS/PostEditor/MacEditorTextView.swift index 601052a..9c11d8a 100644 --- a/macOS/PostEditor/MacEditorTextView.swift +++ b/macOS/PostEditor/MacEditorTextView.swift @@ -163,8 +163,8 @@ final class CustomTextView: NSView { textView.isAutomaticDashSubstitutionEnabled = false textView.isRichText = false textView.typingAttributes = [ - .paragraphStyle: paragraphStyle, // H/T Daniel Jalkut - .font: font, + .paragraphStyle: paragraphStyle, // H/T Daniel Jalkut + .font: font ?? NSFont.systemFont(ofSize: 17), // Fall back to system font if we can't unwrap font argument .foregroundColor: NSColor.labelColor ] diff --git a/macOS/Settings/MacUpdatesView.swift b/macOS/Settings/MacUpdatesView.swift index 53b4f0e..afb6c48 100644 --- a/macOS/Settings/MacUpdatesView.swift +++ b/macOS/Settings/MacUpdatesView.swift @@ -1,16 +1,14 @@ import SwiftUI import Sparkle -enum AppcastFeedUrl: String { - case release = "https://writefreely-files.s3.amazonaws.com/apps/mac/appcast.xml" - case beta = "https://writefreely-files.s3.amazonaws.com/apps/mac/appcast-beta.xml" -} - struct MacUpdatesView: View { + @ObservedObject var updaterViewModel: MacUpdatesViewModel + @AppStorage(WFDefaults.automaticallyChecksForUpdates, store: UserDefaults.shared) var automaticallyChecksForUpdates: Bool = false @AppStorage(WFDefaults.subscribeToBetaUpdates, store: UserDefaults.shared) var subscribeToBetaUpdates: Bool = false + @State private var lastUpdateCheck: Date? private let betaWarningString = """ @@ -34,9 +32,11 @@ that can cause crashes and data loss. VStack { Button(action: { - SUUpdater.shared()?.checkForUpdates(self) - DispatchQueue.main.async { - lastUpdateCheck = SUUpdater.shared()?.lastUpdateCheckDate + updaterViewModel.checkForUpdates() + // There's a delay between requesting an update, and the timestamp for that update request being + // written to user defaults; we therefore delay updating the "Last checked" UI for one second. + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + lastUpdateCheck = updaterViewModel.getLastUpdateCheckDate() } }, label: { Text("Check For Updates") @@ -73,23 +73,19 @@ that can cause crashes and data loss. } .padding() .onAppear { - lastUpdateCheck = SUUpdater.shared()?.lastUpdateCheckDate + lastUpdateCheck = updaterViewModel.getLastUpdateCheckDate() } .onChange(of: automaticallyChecksForUpdates) { value in - SUUpdater.shared()?.automaticallyChecksForUpdates = value + updaterViewModel.automaticallyCheckForUpdates = value } - .onChange(of: subscribeToBetaUpdates) { value in - if value { - SUUpdater.shared()?.feedURL = URL(string: AppcastFeedUrl.beta.rawValue) - } else { - SUUpdater.shared()?.feedURL = URL(string: AppcastFeedUrl.release.rawValue) - } + .onChange(of: subscribeToBetaUpdates) { _ in + updaterViewModel.toggleAllowedChannels() } } } struct MacUpdatesView_Previews: PreviewProvider { static var previews: some View { - MacUpdatesView() + MacUpdatesView(updaterViewModel: MacUpdatesViewModel()) } } diff --git a/macOS/Settings/MacUpdatesViewModel.swift b/macOS/Settings/MacUpdatesViewModel.swift new file mode 100644 index 0000000..b960524 --- /dev/null +++ b/macOS/Settings/MacUpdatesViewModel.swift @@ -0,0 +1,73 @@ +/// See https://sparkle-project.org/documentation/programmatic-setup#create-an-updater-in-swiftui + +import SwiftUI +import Sparkle + +/// This view model class manages Sparkle's updater and publishes when new updates are allowed to be checked. +final class MacUpdatesViewModel: ObservableObject { + + @Published var canCheckForUpdates = false + private let updaterController: SPUStandardUpdaterController + private let updaterDelegate = MacUpdatesViewModelDelegate() + + var automaticallyCheckForUpdates: Bool { + get { + return updaterController.updater.automaticallyChecksForUpdates + } + set(newValue) { + updaterController.updater.automaticallyChecksForUpdates = newValue + } + } + + init() { + updaterController = SPUStandardUpdaterController(startingUpdater: true, + updaterDelegate: updaterDelegate, + userDriverDelegate: nil) + + updaterController.updater.publisher(for: \.canCheckForUpdates) + .assign(to: &$canCheckForUpdates) + + if automaticallyCheckForUpdates { + updaterController.updater.checkForUpdatesInBackground() + } + } + + func checkForUpdates() { + updaterController.checkForUpdates(nil) + } + + func getLastUpdateCheckDate() -> Date? { + return updaterController.updater.lastUpdateCheckDate + } + + @discardableResult + func toggleAllowedChannels() -> Set { + return updaterDelegate.allowedChannels(for: updaterController.updater) + } + +} + +final class MacUpdatesViewModelDelegate: NSObject, SPUUpdaterDelegate { + + @AppStorage(WFDefaults.subscribeToBetaUpdates, store: UserDefaults.shared) + var subscribeToBetaUpdates: Bool = false + + func allowedChannels(for updater: SPUUpdater) -> Set { + let allowedChannels = Set(subscribeToBetaUpdates ? ["beta"] : []) + return allowedChannels + } + +} + +// This additional view is needed for the disabled state on the menu item to work properly before Monterey. +// See https://stackoverflow.com/questions/68553092/menu-not-updating-swiftui-bug for more information +struct CheckForUpdatesView: View { + + @ObservedObject var updaterViewModel: MacUpdatesViewModel + + var body: some View { + Button("Check for Updates…", action: updaterViewModel.checkForUpdates) + .disabled(!updaterViewModel.canCheckForUpdates) + } + +}