From 5c50110d4e657a652df1162024b95ef568994107 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Tue, 22 Dec 2020 10:41:56 -0500 Subject: [PATCH 01/26] Add WriteFreely fork of Sparkle framework via SPM --- .../project.pbxproj | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj index d11a2f9..271f25a 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj +++ b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj @@ -51,6 +51,7 @@ 1765F62B24E18EA200C9EBF0 /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1765F62924E18EA200C9EBF0 /* SidebarView.swift */; }; 17681E412519410E00D394AE /* UINavigationController+Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17681E402519410E00D394AE /* UINavigationController+Appearance.swift */; }; 1780F6EF25895EDB00FE45FF /* PostCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1780F6EE25895EDB00FE45FF /* PostCommands.swift */; }; + 17A4FEDA25924AF70037E96B /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 17A4FED925924AF70037E96B /* Sparkle */; }; 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 */; }; @@ -204,6 +205,7 @@ buildActionMask = 2147483647; files = ( 17DF32C324C87D8D00BCE2E3 /* WriteFreely in Frameworks */, + 17A4FEDA25924AF70037E96B /* Sparkle in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -532,6 +534,7 @@ name = "WriteFreely-MultiPlatform (macOS)"; packageProductDependencies = ( 17DF32C224C87D8D00BCE2E3 /* WriteFreely */, + 17A4FED925924AF70037E96B /* Sparkle */, ); productName = "WriteFreely-MultiPlatform (macOS)"; productReference = 17DF329024C87D3500BCE2E3 /* WriteFreely for Mac.app */; @@ -609,6 +612,7 @@ mainGroup = 17DF327B24C87D3300BCE2E3; packageReferences = ( 17DF32BE24C87D7B00BCE2E3 /* XCRemoteSwiftPackageReference "writefreely-swift" */, + 17A4FED825924AF70037E96B /* XCRemoteSwiftPackageReference "Sparkle" */, ); productRefGroup = 17DF328924C87D3500BCE2E3 /* Products */; projectDirPath = ""; @@ -1176,6 +1180,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 17A4FED825924AF70037E96B /* XCRemoteSwiftPackageReference "Sparkle" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/writefreely/Sparkle"; + requirement = { + branch = master; + kind = branch; + }; + }; 17DF32BE24C87D7B00BCE2E3 /* XCRemoteSwiftPackageReference "writefreely-swift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "git@github.com:writeas/writefreely-swift.git"; @@ -1187,6 +1199,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 17A4FED925924AF70037E96B /* Sparkle */ = { + isa = XCSwiftPackageProductDependency; + package = 17A4FED825924AF70037E96B /* XCRemoteSwiftPackageReference "Sparkle" */; + productName = Sparkle; + }; 17DF32BF24C87D7B00BCE2E3 /* WriteFreely */ = { isa = XCSwiftPackageProductDependency; package = 17DF32BE24C87D7B00BCE2E3 /* XCRemoteSwiftPackageReference "writefreely-swift" */; From 016e10d6b7fabd00ded50411f4312f0f8588ad8a Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Tue, 22 Dec 2020 11:06:06 -0500 Subject: [PATCH 02/26] Add technote on usage of Sparkle --- Technotes/MacSoftwareUpdater.md | 12 ++++++++++++ WriteFreely-MultiPlatform.xcodeproj/project.pbxproj | 2 ++ 2 files changed, 14 insertions(+) create mode 100644 Technotes/MacSoftwareUpdater.md diff --git a/Technotes/MacSoftwareUpdater.md b/Technotes/MacSoftwareUpdater.md new file mode 100644 index 0000000..d38e190 --- /dev/null +++ b/Technotes/MacSoftwareUpdater.md @@ -0,0 +1,12 @@ +# Mac Software Updater + +To make updating the Mac app easy, we're using the [Sparkle framework][1]. + +This is added to the project via the Swift Package Manager (SPM), but at the time of writing, tagged versions of Sparkle do not yet support +SPM — the dependency can only be added from a branch or commit. To avoid any surprises arising from updates to the project's `master` +branch, we're using [WriteFreely's fork of Sparkle][2]. Updates to the forked repository from upstream should be considered dangerous and +tested thoroughly before merging into `main`. + + +[1]: https://sparkle-project.org +[2]: https://github.com/writefreely/Sparkle diff --git a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj index 271f25a..ab91391 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj +++ b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj @@ -146,6 +146,7 @@ 1765F62924E18EA200C9EBF0 /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = ""; }; 17681E402519410E00D394AE /* UINavigationController+Appearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Appearance.swift"; sourceTree = ""; }; 1780F6EE25895EDB00FE45FF /* PostCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCommands.swift; sourceTree = ""; }; + 17A4FEDF25924E810037E96B /* MacSoftwareUpdater.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = MacSoftwareUpdater.md; 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 = ""; }; @@ -230,6 +231,7 @@ isa = PBXGroup; children = ( 1709ADDF251B9A110053AF79 /* EditorLaunchingPolicy.md */, + 17A4FEDF25924E810037E96B /* MacSoftwareUpdater.md */, ); path = Technotes; sourceTree = ""; From 03df62a48cb3b87d65bcb490ead6ec1f053dbafd Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Tue, 22 Dec 2020 15:06:25 -0500 Subject: [PATCH 03/26] Add AppDelegate stub --- Shared/WriteFreely_MultiPlatformApp.swift | 2 ++ WriteFreely-MultiPlatform.xcodeproj/project.pbxproj | 4 ++++ macOS/AppDelegate.swift | 7 +++++++ 3 files changed, 13 insertions(+) create mode 100644 macOS/AppDelegate.swift diff --git a/Shared/WriteFreely_MultiPlatformApp.swift b/Shared/WriteFreely_MultiPlatformApp.swift index 2b249d3..5df3e69 100644 --- a/Shared/WriteFreely_MultiPlatformApp.swift +++ b/Shared/WriteFreely_MultiPlatformApp.swift @@ -20,6 +20,8 @@ struct WriteFreely_MultiPlatformApp: App { @StateObject private var model = WriteFreelyModel() #if os(macOS) + // swiftlint:disable:next weak_delegate + @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @State private var selectedTab = 0 #endif diff --git a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj index ab91391..64bd458 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj +++ b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj @@ -52,6 +52,7 @@ 17681E412519410E00D394AE /* UINavigationController+Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17681E402519410E00D394AE /* UINavigationController+Appearance.swift */; }; 1780F6EF25895EDB00FE45FF /* PostCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1780F6EE25895EDB00FE45FF /* PostCommands.swift */; }; 17A4FEDA25924AF70037E96B /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 17A4FED925924AF70037E96B /* Sparkle */; }; + 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 */; }; 17A5388F24DDEC7400DEFF9A /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388D24DDEC7400DEFF9A /* AccountView.swift */; }; @@ -147,6 +148,7 @@ 17681E402519410E00D394AE /* UINavigationController+Appearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Appearance.swift"; sourceTree = ""; }; 1780F6EE25895EDB00FE45FF /* PostCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCommands.swift; sourceTree = ""; }; 17A4FEDF25924E810037E96B /* MacSoftwareUpdater.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = MacSoftwareUpdater.md; sourceTree = ""; }; + 17A4FEEC25927E730037E96B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.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 = ""; }; @@ -413,6 +415,7 @@ children = ( 17DF329224C87D3500BCE2E3 /* Info.plist */, 17DF329324C87D3500BCE2E3 /* macOS.entitlements */, + 17A4FEEC25927E730037E96B /* AppDelegate.swift */, 17BC617725715042003363CA /* Navigation */, 17A67CAC251A5D8D002F163D /* PostEditor */, 17A5388924DDA50500DEFF9A /* Settings */, @@ -763,6 +766,7 @@ 17DF32AD24C87D3500BCE2E3 /* ContentView.swift in Sources */, 1765F62B24E18EA200C9EBF0 /* SidebarView.swift in Sources */, 1756DBBB24FED45500207AB8 /* LocalStorageManager.swift in Sources */, + 17A4FEED25927E730037E96B /* AppDelegate.swift in Sources */, 174D313324EC2831006CA9EE /* WriteFreelyModel.swift in Sources */, 17D435E924E3128F0036B539 /* PreferencesModel.swift in Sources */, 17120DAA24E1B2F5002B9F6C /* AccountLogoutView.swift in Sources */, diff --git a/macOS/AppDelegate.swift b/macOS/AppDelegate.swift new file mode 100644 index 0000000..3894904 --- /dev/null +++ b/macOS/AppDelegate.swift @@ -0,0 +1,7 @@ +import Cocoa + +class AppDelegate: NSObject, NSApplicationDelegate { + func applicationDidFinishLaunching(_ notification: Notification) { + + } +} From a9eea94d4eece284ffb001763e8509053b96318c Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Tue, 22 Dec 2020 17:02:56 -0500 Subject: [PATCH 04/26] Stub out basic checking for updates in AppDelegate --- macOS/AppDelegate.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/macOS/AppDelegate.swift b/macOS/AppDelegate.swift index 3894904..b44a890 100644 --- a/macOS/AppDelegate.swift +++ b/macOS/AppDelegate.swift @@ -1,7 +1,13 @@ import Cocoa +import Sparkle class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) { - + SUUpdater.shared()?.automaticallyChecksForUpdates = true + /* + Next line prints: + ⚠️ You must specify the URL of the appcast as the SUFeedURL key in either the Info.plist or the user defaults! + */ + SUUpdater.shared()?.checkForUpdates(self) } } From 4eb5d269aa1657217558bc95f9ce444b10a769c2 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Wed, 23 Dec 2020 10:16:02 -0500 Subject: [PATCH 05/26] Add dummy appcast URL to Info.plist --- macOS/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/macOS/Info.plist b/macOS/Info.plist index 0762008..a54867b 100644 --- a/macOS/Info.plist +++ b/macOS/Info.plist @@ -2,6 +2,8 @@ + SUFeedURL + https://writefreely.org/apps/mac/appcast.xml ATSApplicationFontsPath . CFBundleDevelopmentRegion From fb13b9e8ab365d075c8773d3a8778dac58712eaf Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Wed, 23 Dec 2020 10:21:23 -0500 Subject: [PATCH 06/26] =?UTF-8?q?Add=20=E2=80=9CCheck=20For=20Updates?= =?UTF-8?q?=E2=80=9D=20entry=20to=20app=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Shared/WriteFreely_MultiPlatformApp.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Shared/WriteFreely_MultiPlatformApp.swift b/Shared/WriteFreely_MultiPlatformApp.swift index 5df3e69..7e1e0a9 100644 --- a/Shared/WriteFreely_MultiPlatformApp.swift +++ b/Shared/WriteFreely_MultiPlatformApp.swift @@ -1,5 +1,9 @@ import SwiftUI +#if os(macOS) +import Sparkle +#endif + @main struct CheckForDebugModifier { static func main() { @@ -40,6 +44,14 @@ struct WriteFreely_MultiPlatformApp: App { // .preferredColorScheme(preferences.selectedColorScheme) // See PreferencesModel for info. } .commands { + #if os(macOS) + CommandGroup(after: .appInfo, addition: { + Button("Check For Updates") { + print("Checking for updates!") + SUUpdater.shared()?.checkForUpdates(self) + } + }) + #endif CommandGroup(replacing: .newItem, addition: { Button("New Post") { createNewLocalPost() From b05938b49732239ed83d8b297923548a650fe538 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Wed, 23 Dec 2020 11:53:01 -0500 Subject: [PATCH 07/26] Create an updates view for the Preferences window --- Shared/WriteFreely_MultiPlatformApp.swift | 7 +- .../project.pbxproj | 4 + macOS/Settings/MacUpdatesView.swift | 74 +++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 macOS/Settings/MacUpdatesView.swift diff --git a/Shared/WriteFreely_MultiPlatformApp.swift b/Shared/WriteFreely_MultiPlatformApp.swift index 7e1e0a9..f544e74 100644 --- a/Shared/WriteFreely_MultiPlatformApp.swift +++ b/Shared/WriteFreely_MultiPlatformApp.swift @@ -47,7 +47,6 @@ struct WriteFreely_MultiPlatformApp: App { #if os(macOS) CommandGroup(after: .appInfo, addition: { Button("Check For Updates") { - print("Checking for updates!") SUUpdater.shared()?.checkForUpdates(self) } }) @@ -97,6 +96,12 @@ struct WriteFreely_MultiPlatformApp: App { Text("Preferences") } .tag(1) + MacUpdatesView() + .tabItem { + Image(systemName: "arrow.down.circle") + Text("Updates") + } + .tag(2) } .frame(minWidth: 300, maxWidth: 300, minHeight: 200, maxHeight: 200) .padding() diff --git a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj index 64bd458..cc0f2c2 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj +++ b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 17120DB224E1E19C002B9F6C /* SettingsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17120DB124E1E19C002B9F6C /* SettingsHeaderView.swift */; }; 171BFDFA24D4AF8300888236 /* CollectionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF924D4AF8300888236 /* CollectionListView.swift */; }; 171BFDFB24D4AF8300888236 /* CollectionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF924D4AF8300888236 /* CollectionListView.swift */; }; + 172C492E2593981900E20ADF /* MacUpdatesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172C492D2593981900E20ADF /* MacUpdatesView.swift */; }; 173E19D1254318F600440F0F /* RemoteChangePromptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 173E19D0254318F600440F0F /* RemoteChangePromptView.swift */; }; 173E19E3254329CC00440F0F /* PostTextEditingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 173E19E2254329CC00440F0F /* PostTextEditingView.swift */; }; 17466626256C0D0600629997 /* MacEditorTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17466625256C0D0600629997 /* MacEditorTextView.swift */; }; @@ -127,6 +128,7 @@ 17120DAB24E1B99F002B9F6C /* AccountLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountLoginView.swift; sourceTree = ""; }; 17120DB124E1E19C002B9F6C /* SettingsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsHeaderView.swift; sourceTree = ""; }; 171BFDF924D4AF8300888236 /* CollectionListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionListView.swift; sourceTree = ""; }; + 172C492D2593981900E20ADF /* MacUpdatesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacUpdatesView.swift; sourceTree = ""; }; 173E19D0254318F600440F0F /* RemoteChangePromptView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteChangePromptView.swift; sourceTree = ""; }; 173E19E2254329CC00440F0F /* PostTextEditingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostTextEditingView.swift; sourceTree = ""; }; 17466625256C0D0600629997 /* MacEditorTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacEditorTextView.swift; sourceTree = ""; }; @@ -302,6 +304,7 @@ children = ( 17A5388724DDA31F00DEFF9A /* MacAccountView.swift */, 1753F6AB24E431CC00309365 /* MacPreferencesView.swift */, + 172C492D2593981900E20ADF /* MacUpdatesView.swift */, ); path = Settings; sourceTree = ""; @@ -771,6 +774,7 @@ 17D435E924E3128F0036B539 /* PreferencesModel.swift in Sources */, 17120DAA24E1B2F5002B9F6C /* AccountLogoutView.swift in Sources */, 17DF32D624C8CA3400BCE2E3 /* PostStatusBadgeView.swift in Sources */, + 172C492E2593981900E20ADF /* MacUpdatesView.swift in Sources */, 17479F152583D8E40072B7FB /* PostEditorSharingPicker.swift in Sources */, 17480CA6251272EE00EB7765 /* Bundle+AppVersion.swift in Sources */, 17C42E662509237800072984 /* PostListFilteredView.swift in Sources */, diff --git a/macOS/Settings/MacUpdatesView.swift b/macOS/Settings/MacUpdatesView.swift new file mode 100644 index 0000000..b94122d --- /dev/null +++ b/macOS/Settings/MacUpdatesView.swift @@ -0,0 +1,74 @@ +import SwiftUI +import Sparkle + +struct MacUpdatesView: View { + @AppStorage("downloadUpdatesAutomatically") var downloadUpdatesAutomatically: Bool = false + @AppStorage("subscribeToBetaUpdates") var subscribeToBetaUpdates: Bool = false + @State private var lastUpdateCheck: Date? + + private let betaWarningString = """ +Choose release versions to update to the next stable version of WriteFreely. \ +Test versions may have bugs that can cause crashes and data loss. +""" + + static let lastUpdateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateStyle = .short + formatter.timeStyle = .short + formatter.doesRelativeDateFormatting = true + return formatter + }() + + var body: some View { + VStack(spacing: 32) { + VStack { + Text(betaWarningString) + .frame(width: 400) + .foregroundColor(Color(NSColor.placeholderTextColor)) + + Picker(selection: $subscribeToBetaUpdates, label: Text("Download:"), content: { + Text("Release versions").tag(false) + Text("Test versions").tag(true) + }) + .pickerStyle(RadioGroupPickerStyle()) + } + + Button(action: { + SUUpdater.shared()?.checkForUpdates(self) + DispatchQueue.main.async { + lastUpdateCheck = SUUpdater.shared()?.lastUpdateCheckDate + } + }, label: { + Text("Check For Updates") + }) + + VStack { + Toggle(isOn: $downloadUpdatesAutomatically, label: { + Text("Check for updates automatically") + }) + + HStack { + Text("Last check for updates:") + .font(.caption) + if let lastUpdateCheck = lastUpdateCheck { + Text(lastUpdateCheck, formatter: Self.lastUpdateFormatter) + .font(.caption) + } else { + Text("Never") + .font(.caption) + } + } + } + } + .padding() + .onAppear { + lastUpdateCheck = SUUpdater.shared()?.lastUpdateCheckDate + } + } +} + +struct MacUpdatesView_Previews: PreviewProvider { + static var previews: some View { + MacUpdatesView() + } +} From 70dfda7ea06efe3ce3bfab4f769b08fab4054249 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Wed, 23 Dec 2020 14:38:53 -0500 Subject: [PATCH 08/26] Improve layout of Updates/Preferences window --- Shared/WriteFreely_MultiPlatformApp.swift | 2 +- macOS/Settings/MacAccountView.swift | 2 - macOS/Settings/MacUpdatesView.swift | 85 ++++++++++++++--------- 3 files changed, 53 insertions(+), 36 deletions(-) diff --git a/Shared/WriteFreely_MultiPlatformApp.swift b/Shared/WriteFreely_MultiPlatformApp.swift index f544e74..f39b2ef 100644 --- a/Shared/WriteFreely_MultiPlatformApp.swift +++ b/Shared/WriteFreely_MultiPlatformApp.swift @@ -103,7 +103,7 @@ struct WriteFreely_MultiPlatformApp: App { } .tag(2) } - .frame(minWidth: 300, maxWidth: 300, minHeight: 200, maxHeight: 200) + .frame(minWidth: 500, maxWidth: 500, minHeight: 200) .padding() // .preferredColorScheme(preferences.selectedColorScheme) // See PreferencesModel for info. } diff --git a/macOS/Settings/MacAccountView.swift b/macOS/Settings/MacAccountView.swift index e51dfd7..f0d4c30 100644 --- a/macOS/Settings/MacAccountView.swift +++ b/macOS/Settings/MacAccountView.swift @@ -5,9 +5,7 @@ struct MacAccountView: View { var body: some View { Form { - Section(header: Text("Login Details")) { AccountView() - } } } } diff --git a/macOS/Settings/MacUpdatesView.swift b/macOS/Settings/MacUpdatesView.swift index b94122d..deee83b 100644 --- a/macOS/Settings/MacUpdatesView.swift +++ b/macOS/Settings/MacUpdatesView.swift @@ -1,14 +1,19 @@ import SwiftUI import Sparkle +private enum AppcastFeedUrl: String { + case release = "https://files.writefreely.org/apps/mac/appcast.xml" + case beta = "https://files.writefreely.org/apps/mac/appcast-beta.xml" +} + struct MacUpdatesView: View { - @AppStorage("downloadUpdatesAutomatically") var downloadUpdatesAutomatically: Bool = false + @AppStorage("automaticallyChecksForUpdates") var automaticallyChecksForUpdates: Bool = false @AppStorage("subscribeToBetaUpdates") var subscribeToBetaUpdates: Bool = false @State private var lastUpdateCheck: Date? private let betaWarningString = """ -Choose release versions to update to the next stable version of WriteFreely. \ -Test versions may have bugs that can cause crashes and data loss. +To get brand new features before each official release, choose "Test versions." Note that test versions may have bugs \ +that can cause crashes and data loss. """ static let lastUpdateFormatter: DateFormatter = { @@ -20,50 +25,64 @@ Test versions may have bugs that can cause crashes and data loss. }() var body: some View { - VStack(spacing: 32) { - VStack { - Text(betaWarningString) - .frame(width: 400) - .foregroundColor(Color(NSColor.placeholderTextColor)) - - Picker(selection: $subscribeToBetaUpdates, label: Text("Download:"), content: { - Text("Release versions").tag(false) - Text("Test versions").tag(true) - }) - .pickerStyle(RadioGroupPickerStyle()) - } - - Button(action: { - SUUpdater.shared()?.checkForUpdates(self) - DispatchQueue.main.async { - lastUpdateCheck = SUUpdater.shared()?.lastUpdateCheckDate - } - }, label: { - Text("Check For Updates") + VStack(spacing: 24) { + Toggle(isOn: $automaticallyChecksForUpdates, label: { + Text("Check for updates automatically") }) VStack { - Toggle(isOn: $downloadUpdatesAutomatically, label: { - Text("Check for updates automatically") + Button(action: { + SUUpdater.shared()?.checkForUpdates(self) + DispatchQueue.main.async { + lastUpdateCheck = SUUpdater.shared()?.lastUpdateCheckDate + } + }, label: { + Text("Check For Updates") }) - HStack { - Text("Last check for updates:") - .font(.caption) - if let lastUpdateCheck = lastUpdateCheck { - Text(lastUpdateCheck, formatter: Self.lastUpdateFormatter) - .font(.caption) - } else { - Text("Never") + HStack { + Text("Last checked:") .font(.caption) + if let lastUpdateCheck = lastUpdateCheck { + Text(lastUpdateCheck, formatter: Self.lastUpdateFormatter) + .font(.caption) + } else { + Text("Never") + .font(.caption) + } } } + + VStack(spacing: 16) { + HStack(alignment: .top) { + Text("Download:") + Picker(selection: $subscribeToBetaUpdates, label: Text("Download:"), content: { + Text("Release versions").tag(false) + Text("Test versions").tag(true) + }) + .pickerStyle(RadioGroupPickerStyle()) + .labelsHidden() + } + + Text(betaWarningString) + .frame(width: 350) + .foregroundColor(.secondary) } } .padding() .onAppear { lastUpdateCheck = SUUpdater.shared()?.lastUpdateCheckDate } + .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) + } + } } } From a169265707abb0b637cc4a580852f686d90c0da3 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Wed, 23 Dec 2020 14:39:21 -0500 Subject: [PATCH 09/26] Remove SUFeedURL key from Info.plist --- macOS/Info.plist | 2 -- 1 file changed, 2 deletions(-) diff --git a/macOS/Info.plist b/macOS/Info.plist index a54867b..0762008 100644 --- a/macOS/Info.plist +++ b/macOS/Info.plist @@ -2,8 +2,6 @@ - SUFeedURL - https://writefreely.org/apps/mac/appcast.xml ATSApplicationFontsPath . CFBundleDevelopmentRegion From 30b3a446d7d1ab6f9a7e3802a20e6bb06771fa44 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Wed, 23 Dec 2020 15:00:44 -0500 Subject: [PATCH 10/26] Initialize Sparkle based on UserDefaults --- macOS/AppDelegate.swift | 25 ++++++++++++++++++------- macOS/Settings/MacUpdatesView.swift | 2 +- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/macOS/AppDelegate.swift b/macOS/AppDelegate.swift index b44a890..f23d06e 100644 --- a/macOS/AppDelegate.swift +++ b/macOS/AppDelegate.swift @@ -2,12 +2,23 @@ import Cocoa import Sparkle class AppDelegate: NSObject, NSApplicationDelegate { - func applicationDidFinishLaunching(_ notification: Notification) { - SUUpdater.shared()?.automaticallyChecksForUpdates = true - /* - Next line prints: - ⚠️ You must specify the URL of the appcast as the SUFeedURL key in either the Info.plist or the user defaults! - */ - SUUpdater.shared()?.checkForUpdates(self) + 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.standard.bool(forKey: "automaticallyChecksForUpdates") + let subscribeToBetaUpdates = UserDefaults.standard.bool(forKey: "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()?.checkForUpdates(self) + } } } diff --git a/macOS/Settings/MacUpdatesView.swift b/macOS/Settings/MacUpdatesView.swift index deee83b..c72f488 100644 --- a/macOS/Settings/MacUpdatesView.swift +++ b/macOS/Settings/MacUpdatesView.swift @@ -1,7 +1,7 @@ import SwiftUI import Sparkle -private enum AppcastFeedUrl: String { +enum AppcastFeedUrl: String { case release = "https://files.writefreely.org/apps/mac/appcast.xml" case beta = "https://files.writefreely.org/apps/mac/appcast-beta.xml" } From 03a568cfdc918b13e58dd51bedf7365c7bd30961 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Tue, 29 Dec 2020 16:52:26 -0500 Subject: [PATCH 11/26] Add a11y labels/hints to post list and settings --- Shared/PostList/PostListView.swift | 69 +++++++++++-------- .../xcschemes/xcschememanagement.plist | 4 +- iOS/PostEditor/RemoteChangePromptView.swift | 8 +++ iOS/Settings/SettingsHeaderView.swift | 2 + 4 files changed, 54 insertions(+), 29 deletions(-) diff --git a/Shared/PostList/PostListView.swift b/Shared/PostList/PostListView.swift index e20651a..0f9e371 100644 --- a/Shared/PostList/PostListView.swift +++ b/Shared/PostList/PostListView.swift @@ -20,33 +20,44 @@ struct PostListView: View { ) .toolbar { ToolbarItem(placement: .primaryAction) { - Button(action: { - let managedPost = WFAPost(context: self.managedObjectContext) - managedPost.createdDate = Date() - managedPost.title = "" - managedPost.body = "" - managedPost.status = PostStatus.local.rawValue - managedPost.collectionAlias = nil - switch model.preferences.font { - case 1: - managedPost.appearance = "sans" - case 2: - managedPost.appearance = "wrap" - default: - managedPost.appearance = "serif" - } - if let languageCode = Locale.current.languageCode { - managedPost.language = languageCode - managedPost.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft - } - withAnimation { - self.selectedCollection = nil - self.showAllPosts = false - self.model.selectedPost = managedPost - } - }, label: { - Image(systemName: "square.and.pencil") - }) + // We have to add a Spacer as a sibling view to the Button in some kind of Stack, so that any a11y + // modifiers are applied as expected: bug report filed as FB8956392. + ZStack { + Spacer() + Button(action: { + let managedPost = WFAPost(context: self.managedObjectContext) + managedPost.createdDate = Date() + managedPost.title = "" + managedPost.body = "" + managedPost.status = PostStatus.local.rawValue + managedPost.collectionAlias = nil + switch model.preferences.font { + case 1: + managedPost.appearance = "sans" + case 2: + managedPost.appearance = "wrap" + default: + managedPost.appearance = "serif" + } + if let languageCode = Locale.current.languageCode { + managedPost.language = languageCode + managedPost.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft + } + withAnimation { + self.selectedCollection = nil + self.showAllPosts = false + self.model.selectedPost = managedPost + } + }, label: { + Image(systemName: "square.and.pencil") + .scaleEffect(1.25) // These modifiers compensate for the resizing + .padding(.vertical, 12) // done to the Image (and the button tap target) + .padding(.leading, 12) // by the SwiftUI layout system from adding a + .padding(.trailing, 8) // Spacer in this ZStack (FB8956392). + }) + .accessibilityLabel(Text("Compose")) + .accessibilityHint(Text("Compose a new local draft")) + } } ToolbarItem(placement: .bottomBar) { HStack { @@ -55,6 +66,8 @@ struct PostListView: View { }, label: { Image(systemName: "gear") }) + .accessibilityLabel(Text("Settings")) + .accessibilityHint(Text("Open the Settings sheet")) Spacer() Text(postCount == 1 ? "\(postCount) post" : "\(postCount) posts") .foregroundColor(.secondary) @@ -70,6 +83,8 @@ struct PostListView: View { }, label: { Image(systemName: "arrow.clockwise") }) + .accessibilityLabel(Text("Refresh Posts")) + .accessibilityHint(Text("Fetch changes from the server")) .disabled(!model.account.isLoggedIn) } } diff --git a/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist b/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist index 2723ebe..6cd8075 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ WriteFreely-MultiPlatform (iOS).xcscheme_^#shared#^_ orderHint - 0 + 1 WriteFreely-MultiPlatform (macOS).xcscheme_^#shared#^_ orderHint - 1 + 0 diff --git a/iOS/PostEditor/RemoteChangePromptView.swift b/iOS/PostEditor/RemoteChangePromptView.swift index 0807155..184d6b3 100644 --- a/iOS/PostEditor/RemoteChangePromptView.swift +++ b/iOS/PostEditor/RemoteChangePromptView.swift @@ -9,6 +9,8 @@ struct RemoteChangePromptView: View { @Environment(\.horizontalSizeClass) var horizontalSizeClass @State private var promptText: String = "This is placeholder prompt text. Replace it?" @State private var promptIcon: Image = Image(systemName: "questionmark.square.dashed") + @State private var accessibilityLabel: String = "Replace" + @State private var accessibilityHint: String = "Replace this text with an accessibility hint" @State var remoteChangeType: RemotePostChangeType @State var buttonHandler: () -> Void @@ -18,6 +20,8 @@ struct RemoteChangePromptView: View { .font(horizontalSizeClass == .compact ? .caption : .body) .foregroundColor(.secondary) Button(action: buttonHandler, label: { promptIcon }) + .accessibilityLabel(Text(accessibilityLabel)) + .accessibilityHint(Text(accessibilityHint)) } .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20)) .background(Color(UIColor.secondarySystemBackground)) @@ -28,9 +32,13 @@ struct RemoteChangePromptView: View { case .remoteCopyUpdated: promptText = "Newer copy on server. Replace local copy?" promptIcon = Image(systemName: "square.and.arrow.down") + accessibilityLabel = "Update post" + accessibilityHint = "Replace this post with the server version" case .remoteCopyDeleted: promptText = "Post deleted from server. Delete local copy?" promptIcon = Image(systemName: "trash") + accessibilityLabel = "Delete" + accessibilityHint = "Delete this post from your device" } }) } diff --git a/iOS/Settings/SettingsHeaderView.swift b/iOS/Settings/SettingsHeaderView.swift index ca65578..090040b 100644 --- a/iOS/Settings/SettingsHeaderView.swift +++ b/iOS/Settings/SettingsHeaderView.swift @@ -15,6 +15,8 @@ struct SettingsHeaderView: View { }, label: { Image(systemName: "xmark.circle") }) + .accessibilityLabel(Text("Close")) + .accessibilityHint(Text("Dismiss the Settings sheet")) } Text("WriteFreely v\(Bundle.main.appMarketingVersion) (build \(Bundle.main.appBuildVersion))") .font(.caption) From 555b98828290f4ac2d691584d59226b486b50c45 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Tue, 29 Dec 2020 16:53:48 -0500 Subject: [PATCH 12/26] Add a11y label/hint to remote-change prompt on macOS --- Shared/PostEditor/PostEditorStatusToolbarView.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Shared/PostEditor/PostEditorStatusToolbarView.swift b/Shared/PostEditor/PostEditorStatusToolbarView.swift index 7d8cac4..cc02858 100644 --- a/Shared/PostEditor/PostEditorStatusToolbarView.swift +++ b/Shared/PostEditor/PostEditorStatusToolbarView.swift @@ -20,6 +20,8 @@ struct PostEditorStatusToolbarView: View { }, label: { Image(systemName: "square.and.arrow.down") }) + .accessibilityLabel(Text("Update post")) + .accessibilityHint(Text("Replace this post with the server version")) } .padding(.horizontal) .background(Color.primary.opacity(0.1)) @@ -45,6 +47,8 @@ struct PostEditorStatusToolbarView: View { }, label: { Image(systemName: "trash") }) + .accessibilityLabel(Text("Delete")) + .accessibilityHint(Text("Delete this post from your Mac")) } .padding(.horizontal) .background(Color.primary.opacity(0.1)) From f9cce6c9016fc040ed28757d5c56d1e432c89654 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Wed, 30 Dec 2020 09:24:04 -0500 Subject: [PATCH 13/26] Fix button-image sizing and tap target/a11y frame --- Shared/PostList/PostListView.swift | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/Shared/PostList/PostListView.swift b/Shared/PostList/PostListView.swift index 0f9e371..6f7d4fa 100644 --- a/Shared/PostList/PostListView.swift +++ b/Shared/PostList/PostListView.swift @@ -49,11 +49,17 @@ struct PostListView: View { self.model.selectedPost = managedPost } }, label: { - Image(systemName: "square.and.pencil") - .scaleEffect(1.25) // These modifiers compensate for the resizing - .padding(.vertical, 12) // done to the Image (and the button tap target) - .padding(.leading, 12) // by the SwiftUI layout system from adding a - .padding(.trailing, 8) // Spacer in this ZStack (FB8956392). + ZStack { + Image("does.not.exist") + .accessibilityHidden(true) + Image(systemName: "square.and.pencil") + .accessibilityHidden(true) + .imageScale(.large) // These modifiers compensate for the resizing + .padding(.vertical, 12) // done to the Image (and the button tap target) + .padding(.leading, 12) // by the SwiftUI layout system from adding a + .padding(.trailing, 8) // Spacer in this ZStack (FB8956392). + } + .frame(maxWidth: .infinity, maxHeight: .infinity) }) .accessibilityLabel(Text("Compose")) .accessibilityHint(Text("Compose a new local draft")) @@ -65,6 +71,10 @@ struct PostListView: View { model.isPresentingSettingsView = true }, label: { Image(systemName: "gear") + .imageScale(.large) + .padding(.vertical, 12) + .padding(.leading, 8) + .padding(.trailing, 12) }) .accessibilityLabel(Text("Settings")) .accessibilityHint(Text("Open the Settings sheet")) @@ -82,6 +92,10 @@ struct PostListView: View { } }, label: { Image(systemName: "arrow.clockwise") + .imageScale(.large) + .padding(.vertical, 12) + .padding(.leading, 12) + .padding(.trailing, 8) }) .accessibilityLabel(Text("Refresh Posts")) .accessibilityHint(Text("Fetch changes from the server")) From 7d99c8afc46ab211903342fc6b0d570772000cd2 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Wed, 30 Dec 2020 10:03:53 -0500 Subject: [PATCH 14/26] =?UTF-8?q?Add=20=E2=80=9Cdoes.not.exist=E2=80=9D=20?= =?UTF-8?q?1x1=20transparent=20image?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to silence runtime warnings about missing images for the workaround used to add a11y labels/hints to toolbar buttons. --- .../does.not.exist.imageset/Contents.json | 23 ++++++++++++++++++ .../does.not.exist.png | Bin 0 -> 501 bytes .../does.not.exist@2x.png | Bin 0 -> 501 bytes .../does.not.exist@3x.png | Bin 0 -> 502 bytes 4 files changed, 23 insertions(+) create mode 100644 Shared/Assets.xcassets/does.not.exist.imageset/Contents.json create mode 100644 Shared/Assets.xcassets/does.not.exist.imageset/does.not.exist.png create mode 100644 Shared/Assets.xcassets/does.not.exist.imageset/does.not.exist@2x.png create mode 100644 Shared/Assets.xcassets/does.not.exist.imageset/does.not.exist@3x.png diff --git a/Shared/Assets.xcassets/does.not.exist.imageset/Contents.json b/Shared/Assets.xcassets/does.not.exist.imageset/Contents.json new file mode 100644 index 0000000..f89ec02 --- /dev/null +++ b/Shared/Assets.xcassets/does.not.exist.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "does.not.exist.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "does.not.exist@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "does.not.exist@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Shared/Assets.xcassets/does.not.exist.imageset/does.not.exist.png b/Shared/Assets.xcassets/does.not.exist.imageset/does.not.exist.png new file mode 100644 index 0000000000000000000000000000000000000000..6238655077803c0c0c18c3579a05886d62c448c8 GIT binary patch literal 501 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1|%|szQ_Zq#7t-BfNW=X=lq=fqTqtWt|u+~aJ75A%WQ+Iiy78Ux$-DQ;V*kf z@MP{TF9`{W;D(&1pC&O0>}Eb8rpaW*bz&ZK(sJh;(hhG_IDT_P_0qtBFy8Q z2g|I3KbTKk^K=sOcTaMD%gUHH{mi4LIY+!&8IqaSsWMMl5;mnF-QnR;FB4M{Q*COU(Ktg>x3}@(O0fp7k>jo-Xb-^GtJkR zK?}&{U|?*|WMF~Di4>4#U|?Fn1Q%gkz>HvnByYXie*{Qzd%8G=aL6VnFfcJNure_A S?wOO|GKyppK57poIDp2l!g{@FekYb)G!R z+Q-Of=b*}CASGI}``7`FMf$Is&RD6MZuQ(6ci?)`vJY3g*SpL%xVo5O-IObjQWXBO zcLY!7?(&k5kO*$bdHQJ*lfZ7~6JnZ7R$M3MF()l|z9H@KMup=yM})7(1mg*{%qPM; z&UvuRI{1V6#5GSRA%FKI=eMkkdDG84YMOJztCb;{X`L$blqF$P8Zw^$X*#v&%$FV^ zR`&#rrCJrwRA0Z;kdTy^Wo*!JNM_NmVg(Bw$)o2Kj?8**Y;fb?O%wui|m2PtC zY;4!6|NpNK0|wOmGY3ADANv1)zxdz(|H};nKFhMTF;_B(DyF_OJ$~^wFyt*#BRtc5 zeHpZXYz_v-_DlvAAd3-*rGOY1GYgpDB1{XI5p0m;xeN0q04Z)y7sn6|+2n)+K#nj2 UqsY;!jX)NIr>mdKI;Vst0Cxkh)&Kwi literal 0 HcmV?d00001 diff --git a/Shared/Assets.xcassets/does.not.exist.imageset/does.not.exist@3x.png b/Shared/Assets.xcassets/does.not.exist.imageset/does.not.exist@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..364bf5d1d1ad835f04800398d1c9d122a02fc8c2 GIT binary patch literal 502 zcmeAS@N?(olHy`uVBq!ia0vp^%plCc#=yX+sNbpyq!KfooddF+-JSDu@{58C5|dM1 zGK&jx5-S6Y85lGsCMP5WdFp7KNK8yfIB`~6ldGsasH3AND53tu0lt?S4*p<1am~|7$lpE5`7JAB-t;q%n&uqwYGp`fTBpi9Wl7kShK%Qbnocb`^QA|K z)jdIDsaC}^)z>dIBqSwf85=Yll3DbtSiwR^^5{8*BeUKc8{ByJ9|-bVW?ZjhYwLBG z8)Ge)Bqhef({zW2N4-o;MNGA+bw=Y1P2JwchnI`k*zS3-u|4E=u)TOR^O##grJI~O z8{4(&|Nra5fB`lC%z+Q(hyMTHFaG!c|8m2C&$4W7%#{qHimC5Rk6-)^40(&x2+uTM zUj{88n}dO|J(Gb2$YKOyDIjKGU|PTg7hzt&j9`N#OT=oDffSFYi(?3fY;uBv7?7vM U!1&>a{0or0r>mdKI;Vst0KP@96#xJL literal 0 HcmV?d00001 From 608c66e54301d7282800ffe0b47cae8c56af076a Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Wed, 30 Dec 2020 10:19:25 -0500 Subject: [PATCH 15/26] Add a11y labels/hints to post editor textviews MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s impossible to differentiate these textviews because the placeholder text is not read by VoiceOver, so we need to add labels. --- .../angelo.xcuserdatad/xcschemes/xcschememanagement.plist | 4 ++-- iOS/PostEditor/PostTextEditingView.swift | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist b/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist index 6cd8075..2723ebe 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ WriteFreely-MultiPlatform (iOS).xcscheme_^#shared#^_ orderHint - 1 + 0 WriteFreely-MultiPlatform (macOS).xcscheme_^#shared#^_ orderHint - 0 + 1 diff --git a/iOS/PostEditor/PostTextEditingView.swift b/iOS/PostEditor/PostTextEditingView.swift index a58fbae..18b70fb 100644 --- a/iOS/PostEditor/PostTextEditingView.swift +++ b/iOS/PostEditor/PostTextEditingView.swift @@ -49,6 +49,8 @@ struct PostTextEditingView: View { isFirstResponder: $titleIsFirstResponder, lineSpacing: horizontalSizeClass == .compact ? lineSpacingMultiplier / 2 : lineSpacingMultiplier ) + .accessibilityLabel(Text("Title (optional)")) + .accessibilityHint(Text("Add or edit the title for your post; use the Return key to skip to the body")) .frame(height: titleFieldHeight) .onChange(of: post.title) { _ in if post.status == PostStatus.published.rawValue && !updatingTitleFromServer { @@ -73,6 +75,8 @@ struct PostTextEditingView: View { isFirstResponder: $bodyIsFirstResponder, lineSpacing: horizontalSizeClass == .compact ? lineSpacingMultiplier / 2 : lineSpacingMultiplier ) + .accessibilityLabel(Text("Body")) + .accessibilityHint(Text("Add or edit the body of your post")) .onChange(of: post.body) { _ in if post.status == PostStatus.published.rawValue && !updatingBodyFromServer { post.status = PostStatus.edited.rawValue From 69cd86c1da81f168672ead3b85a29a3f079f40a1 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Wed, 30 Dec 2020 10:34:54 -0500 Subject: [PATCH 16/26] Prevent VO from reading the placeholder-text views --- iOS/PostEditor/PostTextEditingView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iOS/PostEditor/PostTextEditingView.swift b/iOS/PostEditor/PostTextEditingView.swift index 18b70fb..51f5ed2 100644 --- a/iOS/PostEditor/PostTextEditingView.swift +++ b/iOS/PostEditor/PostTextEditingView.swift @@ -41,6 +41,7 @@ struct PostTextEditingView: View { .foregroundColor(Color(UIColor.placeholderText)) .padding(.horizontal, 4) .padding(.vertical, 8) + .accessibilityHidden(true) } PostTitleTextView( text: $post.title, @@ -68,6 +69,7 @@ struct PostTextEditingView: View { .foregroundColor(Color(UIColor.placeholderText)) .padding(.horizontal, 4) .padding(.vertical, 8) + .accessibilityHidden(true) } PostBodyTextView( text: $post.body, From 34c2cd181e7af597dc465a1f3c4cd1ed5b67e750 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Wed, 30 Dec 2020 10:39:48 -0500 Subject: [PATCH 17/26] Add a11y features to post editor context menu Adding an .onTapGesture modifier here to dismiss the keyboard removes focus from whatever textview is active in the post editor, which prevents VoiceOver from getting confused about which accessibility frame should be read. --- iOS/PostEditor/PostEditorView.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/iOS/PostEditor/PostEditorView.swift b/iOS/PostEditor/PostEditorView.swift index e5dfbd7..3421fc4 100644 --- a/iOS/PostEditor/PostEditorView.swift +++ b/iOS/PostEditor/PostEditorView.swift @@ -117,8 +117,19 @@ struct PostEditorView: View { } } }, label: { - Image(systemName: "ellipsis.circle") + ZStack { + Image("does.not.exist") + .accessibilityHidden(true) + Image(systemName: "ellipsis.circle") + .imageScale(.large) + .accessibilityHidden(true) + } }) + .accessibilityLabel(Text("Menu")) + .accessibilityHint(Text("Opens a context menu to publish, share, or move the post")) + .onTapGesture { + hideKeyboard() + } } } } From e68030ec717d19daf770c552fb2b403506f5005a Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Wed, 30 Dec 2020 14:49:07 -0500 Subject: [PATCH 18/26] Improve a11y labels on post editor menu --- iOS/PostEditor/PostEditorView.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iOS/PostEditor/PostEditorView.swift b/iOS/PostEditor/PostEditorView.swift index 3421fc4..d866cc7 100644 --- a/iOS/PostEditor/PostEditorView.swift +++ b/iOS/PostEditor/PostEditorView.swift @@ -80,6 +80,8 @@ struct PostEditorView: View { }, label: { Label("Publish…", systemImage: "paperplane") }) + .accessibilityHint(Text("Choose the blog you want to publish this post to")) + .disabled(post.body.count == 0) } else { Button(action: { if model.account.isLoggedIn { @@ -97,6 +99,7 @@ struct PostEditorView: View { }, label: { Label("Share", systemImage: "square.and.arrow.up") }) + .accessibilityHint(Text("Open the system share sheet to share a link to this post")) .disabled(post.postId == nil) // Button(action: { // print("Tapped 'Delete...' button") @@ -130,6 +133,7 @@ struct PostEditorView: View { .onTapGesture { hideKeyboard() } + .disabled(post.body.count == 0) } } } From 724478a80afe8d7ae05d5e84cea5c76ed9eb6597 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Mon, 4 Jan 2021 14:25:02 -0500 Subject: [PATCH 19/26] Add link to write a review in the App Store --- Shared/Models/WriteFreelyModel.swift | 1 + .../angelo.xcuserdatad/xcschemes/xcschememanagement.plist | 4 ++-- iOS/Settings/SettingsView.swift | 7 ++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Shared/Models/WriteFreelyModel.swift b/Shared/Models/WriteFreelyModel.swift index b53da10..8cb36dd 100644 --- a/Shared/Models/WriteFreelyModel.swift +++ b/Shared/Models/WriteFreelyModel.swift @@ -29,6 +29,7 @@ class WriteFreelyModel: ObservableObject { // swiftlint:disable line_length let helpURL = URL(string: "https://discuss.write.as/c/help/5")! let howToURL = URL(string: "https://discuss.write.as/t/using-the-writefreely-ios-app/1946")! + let reviewURL = URL(string: "https://apps.apple.com/app/id1531530896?action=write-review")! let licensesURL = URL(string: "https://github.com/writeas/writefreely-swiftui-multiplatform/tree/main/Shared/Resources/Licenses")! // swiftlint:enable line_length diff --git a/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist b/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist index 2723ebe..6cd8075 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ WriteFreely-MultiPlatform (iOS).xcscheme_^#shared#^_ orderHint - 0 + 1 WriteFreely-MultiPlatform (macOS).xcscheme_^#shared#^_ orderHint - 1 + 0 diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift index 0d825f4..e64bc94 100644 --- a/iOS/Settings/SettingsView.swift +++ b/iOS/Settings/SettingsView.swift @@ -13,7 +13,7 @@ struct SettingsView: View { Section(header: Text("Appearance")) { PreferencesView(preferences: model.preferences) } - Section(header: Text("Support Links")) { + Section(header: Text("External Links")) { HStack { Spacer() Link("View the Guide", destination: model.howToURL) @@ -24,6 +24,11 @@ struct SettingsView: View { Link("Visit the Help Forum", destination: model.helpURL) Spacer() } + HStack { + Spacer() + Link("Write a Review on the App Store", destination: model.reviewURL) + Spacer() + } } Section(header: Text("Acknowledgements")) { VStack { From d08daa95af233307382042e597bc7e74a54a9f2f Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Tue, 5 Jan 2021 10:00:05 -0500 Subject: [PATCH 20/26] Add Sparkle feed URL and public key --- macOS/Info.plist | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/macOS/Info.plist b/macOS/Info.plist index 0762008..b29e021 100644 --- a/macOS/Info.plist +++ b/macOS/Info.plist @@ -2,6 +2,10 @@ + SUFeedURL + https://files.writefreely.org/apps/mac/appcast.xml + SUPublicEDKey + xLenuurDaQb2/dj2ScylLmJx0gSnBmacUsOAgUjErUc= ATSApplicationFontsPath . CFBundleDevelopmentRegion From 749d5235e28d142a2392acd1c53055050ea840cc Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Tue, 5 Jan 2021 15:02:52 -0500 Subject: [PATCH 21/26] Update feed URL --- macOS/Info.plist | 8 ++++---- macOS/Settings/MacUpdatesView.swift | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/macOS/Info.plist b/macOS/Info.plist index b29e021..5b2a81e 100644 --- a/macOS/Info.plist +++ b/macOS/Info.plist @@ -2,10 +2,6 @@ - SUFeedURL - https://files.writefreely.org/apps/mac/appcast.xml - SUPublicEDKey - xLenuurDaQb2/dj2ScylLmJx0gSnBmacUsOAgUjErUc= ATSApplicationFontsPath . CFBundleDevelopmentRegion @@ -32,5 +28,9 @@ public.app-category.social-networking LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) + SUFeedURL + https://writefreely-files.s3.amazonaws.com/apps/mac/appcast.xml + SUPublicEDKey + xLenuurDaQb2/dj2ScylLmJx0gSnBmacUsOAgUjErUc= diff --git a/macOS/Settings/MacUpdatesView.swift b/macOS/Settings/MacUpdatesView.swift index c72f488..45f682a 100644 --- a/macOS/Settings/MacUpdatesView.swift +++ b/macOS/Settings/MacUpdatesView.swift @@ -2,8 +2,8 @@ import SwiftUI import Sparkle enum AppcastFeedUrl: String { - case release = "https://files.writefreely.org/apps/mac/appcast.xml" - case beta = "https://files.writefreely.org/apps/mac/appcast-beta.xml" + 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 { From 2b826e02236c7b7354fc920347b7fab3b935e852 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Tue, 5 Jan 2021 15:03:42 -0500 Subject: [PATCH 22/26] Remove sandboxing for Sparkle compatibility --- macOS/macOS.entitlements | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/macOS/macOS.entitlements b/macOS/macOS.entitlements index 40b639e..0c67376 100644 --- a/macOS/macOS.entitlements +++ b/macOS/macOS.entitlements @@ -1,14 +1,5 @@ - - com.apple.security.app-sandbox - - com.apple.security.files.user-selected.read-only - - com.apple.security.network.client - - com.apple.security.network.server - - + From 9f1e125a18e02dc6cb73e90b218ecfc0491ec24a Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Tue, 5 Jan 2021 15:20:14 -0500 Subject: [PATCH 23/26] Add Sparkle to the credits doc --- Shared/Resources/Licenses/Sparkle-License.txt | 61 +++++++++++++++++++ .../project.pbxproj | 2 + macOS/Credits.rtf | 9 +-- 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 Shared/Resources/Licenses/Sparkle-License.txt diff --git a/Shared/Resources/Licenses/Sparkle-License.txt b/Shared/Resources/Licenses/Sparkle-License.txt new file mode 100644 index 0000000..1e9b1c6 --- /dev/null +++ b/Shared/Resources/Licenses/Sparkle-License.txt @@ -0,0 +1,61 @@ +Copyright (c) 2006-2013 Andy Matuschak. +Copyright (c) 2009-2013 Elgato Systems GmbH. +Copyright (c) 2011-2014 Kornel Lesiński. +Copyright (c) 2015-2017 Mayur Pawashe. +Copyright (c) 2014 C.W. Betts. +Copyright (c) 2014 Petroules Corporation. +Copyright (c) 2014 Big Nerd Ranch. +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +================= +EXTERNAL LICENSES +================= + +bspatch.c and bsdiff.c, from bsdiff 4.3 : + Copyright (c) 2003-2005 Colin Percival. + +sais.c and sais.c, from sais-lite (2010/08/07) : + Copyright (c) 2008-2010 Yuta Mori. + +SUDSAVerifier.m: + Copyright (c) 2011 Mark Hamlin. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted providing that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj index cc0f2c2..77b20a4 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj +++ b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj @@ -160,6 +160,7 @@ 17AD0A6325489E900057D763 /* PostBodyTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostBodyTextView.swift; sourceTree = ""; }; 17B3E964250FAA9000EE9748 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; 17B5103A2515448D00E9631F /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = ""; }; + 17B68D4F25A4FED2005ED37C /* Sparkle-License.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "Sparkle-License.txt"; sourceTree = ""; }; 17B996D62502D23E0017B536 /* WFAPost+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WFAPost+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; }; 17B996D72502D23E0017B536 /* WFAPost+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WFAPost+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; }; 17BC617825715068003363CA /* ActivePostToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivePostToolbarView.swift; sourceTree = ""; }; @@ -495,6 +496,7 @@ 17DFDE83251D309400A25F31 /* Licenses */ = { isa = PBXGroup; children = ( + 17B68D4F25A4FED2005ED37C /* Sparkle-License.txt */, 17DFDE84251D309400A25F31 /* Hack-License.txt */, 17DFDE85251D309400A25F31 /* Lora-Cyrillic-OFL.txt */, 17DFDE86251D309400A25F31 /* OpenSans-License.txt */, diff --git a/macOS/Credits.rtf b/macOS/Credits.rtf index 17b79ca..b8dad72 100644 --- a/macOS/Credits.rtf +++ b/macOS/Credits.rtf @@ -7,12 +7,13 @@ \margl1440\margr1440\vieww9000\viewh8400\viewkind0 \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 -\f0\fs26 \cf0 This application makes use of the following open-source projects:\ +\f0\fs26 \cf0 WriteFreely for Mac makes use of the following open-source projects:\ \ \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural\partightenfactor0 -\ls1\ilvl0\cf0 {\listtext \uc0\u8226 }Lora typeface\ -{\listtext \uc0\u8226 }Open Sans typeface\ -{\listtext \uc0\u8226 }Hack typeface\ +\ls1\ilvl0\cf0 {\listtext \uc0\u8226 }Lora ({\field{\*\fldinst{HYPERLINK "https://github.com/cyrealtype/Lora-Cyrillic"}}{\fldrslt typeface}})\ +{\listtext \uc0\u8226 }Open Sans ({\field{\*\fldinst{HYPERLINK "https://fonts.google.com/specimen/Open+Sans"}}{\fldrslt typeface}})\ +{\listtext \uc0\u8226 }Hack ({\field{\*\fldinst{HYPERLINK "https://sourcefoundry.org/hack/"}}{\fldrslt typeface}})\ +{\listtext \uc0\u8226 }Sparkle ({\field{\*\fldinst{HYPERLINK "https://sparkle-project.org"}}{\fldrslt framework}})\ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 \cf0 \ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 From 86d10887f48a588c7a2bb6bd700af440fac5fe75 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Tue, 5 Jan 2021 16:30:15 -0500 Subject: [PATCH 24/26] Add troubleshooting/deploy details to technote --- Technotes/MacSoftwareUpdater.md | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/Technotes/MacSoftwareUpdater.md b/Technotes/MacSoftwareUpdater.md index d38e190..690f3b8 100644 --- a/Technotes/MacSoftwareUpdater.md +++ b/Technotes/MacSoftwareUpdater.md @@ -7,6 +7,60 @@ SPM — the dependency can only be added from a branch or commit. To avoid any s branch, we're using [WriteFreely's fork of Sparkle][2]. Updates to the forked repository from upstream should be considered dangerous and tested thoroughly before merging into `main`. +WriteFreely for Mac uses the v1.x branch of Sparkle, and is therefore not a sandboxed app. + +## Troubleshooting + +### If Xcode throws an error when you try to build the project + +You may need to reset the package caches: + +1. From the **File** menu in Xcode, choose **Swift Packages** → **Reset Package Caches** +2. Again from the **File** menu, choose **Swift Packages** → **Update to Latest Package Versions** + +You should then be able to build and run the Mac target. + +### If you can't run `generate_keys` because "Apple cannot check it for malicious software" + +There may be a code signing issue with Sparkle. Right-click on `generate_keys` in the Finder and choose Open ([reference][3]). + +## Deploying Updates + +To [publish an update to the app][5], you'll need the **Sparkle-for-Swift-Package-Manager.zip** [archive][4] — specifically, you'll need the +`generate_appcast` tool. Download and de-compress the archive. + +You will need some credentials and signing certificates to proceed with this process; speak to the project maintainer if you're responsible for +creating the update, and confirm you have: + +- the app's Developer ID Application certificate (check your Mac's system Keychain) +- the Sparkle EdDSA signing key (again, check your Mac's system Keychain) + +Sign and notarize the app archive, then click on **Export Notarized App** in Xcode's Organizer window. Open the Terminal and navigate to +where you de-compressed the Sparkle-for-Swift-Package-Manager archive, then create a zip file that preserves symlinks: + +```bash +% ditto -c -k --sequesterRsrc --keepParent +``` + +For example, if you export the notarized app to the desktop, all prior updates are located in `~/Developer/WriteFreely/Updates`, and +the final archive should be called `WFMac.zip`, you would run: + +```bash +% ditto -c -k --sequesterRsrc --keepParent ~/Desktop/WriteFreely\ for\ Mac.app ~/Developer/WriteFreely/Updates/WFMac.zip +``` + +Then, generate an appcast file: + +```bash +% ./bin/generate_appcast ~/Developer/WriteFreely/Updates +``` + +Once that's done, upload the appcast.xml and WFMac.zip files to the update distribution server (`files.writefreely.org/apps/mac`) +and they'll be made available to users. + [1]: https://sparkle-project.org [2]: https://github.com/writefreely/Sparkle +[3]: https://github.com/sparkle-project/Sparkle/issues/1701#issuecomment-752249920 +[4]: https://github.com/sparkle-project/Sparkle/releases/tag/1.24.0 +[5]: https://sparkle-project.org/documentation/publishing/ From 5a34019d4c8a4e6702c587d6f7a4e5a0f4d3af59 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Thu, 7 Jan 2021 15:33:22 -0500 Subject: [PATCH 25/26] Bump version and build number for Mac app --- WriteFreely-MultiPlatform.xcodeproj/project.pbxproj | 8 ++++---- .../angelo.xcuserdatad/xcschemes/xcschememanagement.plist | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj index 4d26050..f06d1e3 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj +++ b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj @@ -1008,7 +1008,7 @@ CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 514; + CURRENT_PROJECT_VERSION = 531; DEVELOPMENT_TEAM = TPPAB4YBA6; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; @@ -1018,7 +1018,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0b1; + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform"; PRODUCT_NAME = "WriteFreely for Mac"; SDKROOT = macosx; @@ -1034,7 +1034,7 @@ CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 514; + CURRENT_PROJECT_VERSION = 531; DEVELOPMENT_TEAM = TPPAB4YBA6; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; @@ -1044,7 +1044,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0b1; + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform"; PRODUCT_NAME = "WriteFreely for Mac"; SDKROOT = macosx; diff --git a/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist b/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist index 2723ebe..6cd8075 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ WriteFreely-MultiPlatform (iOS).xcscheme_^#shared#^_ orderHint - 0 + 1 WriteFreely-MultiPlatform (macOS).xcscheme_^#shared#^_ orderHint - 1 + 0 From 86dc41c0bc3e2ad32e58b731b45d30c280777923 Mon Sep 17 00:00:00 2001 From: Angelo Stavrow Date: Thu, 7 Jan 2021 15:53:01 -0500 Subject: [PATCH 26/26] Correct Mac version number and add beta verbiage --- WriteFreely-MultiPlatform.xcodeproj/project.pbxproj | 8 ++++---- macOS/Credits.rtf | 11 ++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj index f06d1e3..1f506a6 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj +++ b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj @@ -1008,7 +1008,7 @@ CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 531; + CURRENT_PROJECT_VERSION = 532; DEVELOPMENT_TEAM = TPPAB4YBA6; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; @@ -1018,7 +1018,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 0.1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform"; PRODUCT_NAME = "WriteFreely for Mac"; SDKROOT = macosx; @@ -1034,7 +1034,7 @@ CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 531; + CURRENT_PROJECT_VERSION = 532; DEVELOPMENT_TEAM = TPPAB4YBA6; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; @@ -1044,7 +1044,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 0.1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform"; PRODUCT_NAME = "WriteFreely for Mac"; SDKROOT = macosx; diff --git a/macOS/Credits.rtf b/macOS/Credits.rtf index b8dad72..ed4d885 100644 --- a/macOS/Credits.rtf +++ b/macOS/Credits.rtf @@ -1,13 +1,18 @@ {\rtf1\ansi\ansicpg1252\cocoartf2578 -\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 SFProDisplay-Regular;} +\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 SFProDisplay-Bold;\f1\fnil\fcharset0 SFProDisplay-Regular;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} {\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid1\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid1}} {\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}} \margl1440\margr1440\vieww9000\viewh8400\viewkind0 -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc\partightenfactor0 -\f0\fs26 \cf0 WriteFreely for Mac makes use of the following open-source projects:\ +\f0\b\fs26 \cf0 \ +PRE-RELEASE SOFTWARE +\f1\b0 \ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 +\cf0 \ +WriteFreely for Mac makes use of the following open-source projects:\ \ \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural\partightenfactor0 \ls1\ilvl0\cf0 {\listtext \uc0\u8226 }Lora ({\field{\*\fldinst{HYPERLINK "https://github.com/cyrealtype/Lora-Cyrillic"}}{\fldrslt typeface}})\