Clean up project warnings (#199)
* Set "recommended project settings" for Mac app
* Add default fallback for font typingAttribute
* Clean up linter errors
* Update extension iconset
* Fix spm errors and upgrade sparkle (#203)
* Switch WriteFreely package import from ssh to https
* Sign macOS project target for Development
This fixes a crash-on-launch bug when running Product > Run from Xcode (Product > Archive should work correctly). See https://github.com/sparkle-project/Sparkle/issues/2056 for more details.
Bug introduced by commit:
671b0540ac
* Update Sparkle to v2.0 from official repo
Previously, we forked the repo to the WriteFreely org because Sparkle didn't support tag-based SPM updates. We therefore had to pull from the `master` branch, so forking insulated us from unexpected changes.
As of 2.x Sparkle now allows for tag-based updating via SPM; we'll allow patch updates but stop short of 2.1 until we can vet changes.
* Add MacUpdatesViewModel ObservableObject
* Use MacUpdatesViewModel to handle Sparkle update logic
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 7.3 KiB |
@ -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"
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 763 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 524 B |
After Width: | Height: | Size: 1.0 KiB |
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)")
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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 = "<group>"; };
|
||||
17C42E6F250AA12200072984 /* NSManagedObjectContext+ExecuteAndMergeChanges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+ExecuteAndMergeChanges.swift"; sourceTree = "<group>"; };
|
||||
17D435E724E3128F0036B539 /* PreferencesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesModel.swift; sourceTree = "<group>"; };
|
||||
17D4926627947D780035BD7E /* MacUpdatesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacUpdatesViewModel.swift; sourceTree = "<group>"; };
|
||||
17D4F36B2514EE2F00517CE6 /* LoraGX.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = LoraGX.ttf; sourceTree = "<group>"; };
|
||||
17D4F39D2514F0E500517CE6 /* OpenSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "OpenSans-Regular.ttf"; sourceTree = "<group>"; };
|
||||
17D4F3A42514F1E900517CE6 /* Hack-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Hack-Regular.ttf"; sourceTree = "<group>"; };
|
||||
@ -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 = "<group>";
|
||||
@ -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 */
|
||||
|
@ -7,17 +7,17 @@
|
||||
<key>ActionExtension-iOS.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>WriteFreely-MultiPlatform (iOS).xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<key>WriteFreely-MultiPlatform (macOS).xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
|
@ -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)
|
||||
|
@ -164,7 +164,7 @@ final class CustomTextView: NSView {
|
||||
textView.isRichText = false
|
||||
textView.typingAttributes = [
|
||||
.paragraphStyle: paragraphStyle, // H/T Daniel Jalkut
|
||||
.font: font,
|
||||
.font: font ?? NSFont.systemFont(ofSize: 17), // Fall back to system font if we can't unwrap font argument
|
||||
.foregroundColor: NSColor.labelColor
|
||||
]
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
.onChange(of: subscribeToBetaUpdates) { value in
|
||||
if value {
|
||||
SUUpdater.shared()?.feedURL = URL(string: AppcastFeedUrl.beta.rawValue)
|
||||
} else {
|
||||
SUUpdater.shared()?.feedURL = URL(string: AppcastFeedUrl.release.rawValue)
|
||||
updaterViewModel.automaticallyCheckForUpdates = value
|
||||
}
|
||||
.onChange(of: subscribeToBetaUpdates) { _ in
|
||||
updaterViewModel.toggleAllowedChannels()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MacUpdatesView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
MacUpdatesView()
|
||||
MacUpdatesView(updaterViewModel: MacUpdatesViewModel())
|
||||
}
|
||||
}
|
||||
|
73
macOS/Settings/MacUpdatesViewModel.swift
Normal file
@ -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<String> {
|
||||
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<String> {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|