Page MenuHomeMusing Studio

No OneTemporary

diff --git a/Shared/WriteFreely_MultiPlatformApp.swift b/Shared/WriteFreely_MultiPlatformApp.swift
index ea741c8..4378ada 100644
--- a/Shared/WriteFreely_MultiPlatformApp.swift
+++ b/Shared/WriteFreely_MultiPlatformApp.swift
@@ -1,154 +1,146 @@
import SwiftUI
#if os(macOS)
import Sparkle
#endif
@main
struct CheckForDebugModifier {
static func main() {
#if os(macOS)
if NSEvent.modifierFlags.contains(.shift) {
// Clear the launch-to-last-draft values to load a new draft.
UserDefaults.shared.setValue(false, forKey: WFDefaults.showAllPostsFlag)
UserDefaults.shared.setValue(nil, forKey: WFDefaults.selectedCollectionURL)
UserDefaults.shared.setValue(nil, forKey: WFDefaults.lastDraftURL)
} else {
// No-op
}
#endif
WriteFreely_MultiPlatformApp.main()
}
}
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
var body: some Scene {
WindowGroup {
ContentView()
.onAppear(perform: {
if model.editor.showAllPostsFlag {
DispatchQueue.main.async {
self.model.selectedCollection = nil
self.model.showAllPosts = true
showLastDraftOrCreateNewLocalPost()
}
} else {
DispatchQueue.main.async {
self.model.selectedCollection = model.editor.fetchSelectedCollectionFromAppStorage()
self.model.showAllPosts = false
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)
// .preferredColorScheme(preferences.selectedColorScheme) // See PreferencesModel for info.
}
.commands {
#if os(macOS)
CommandGroup(after: .appInfo) {
CheckForUpdatesView(updaterViewModel: updaterViewModel)
}
#endif
CommandGroup(replacing: .newItem, addition: {
Button("New Post") {
createNewLocalPost()
}
.keyboardShortcut("n", modifiers: [.command])
})
CommandGroup(after: .newItem) {
Button("Refresh Posts") {
DispatchQueue.main.async {
model.fetchUserCollections()
model.fetchUserPosts()
}
}
.disabled(!model.account.isLoggedIn)
.keyboardShortcut("r", modifiers: [.command])
}
SidebarCommands()
#if os(macOS)
PostCommands(model: model)
#endif
CommandGroup(after: .help) {
Button("Visit Support Forum") {
#if os(macOS)
NSWorkspace().open(model.helpURL)
#else
UIApplication.shared.open(model.helpURL)
#endif
}
}
ToolbarCommands()
TextEditingCommands()
}
#if os(macOS)
Settings {
TabView(selection: $selectedTab) {
MacAccountView()
.environmentObject(model)
.tabItem {
Image(systemName: "person.crop.circle")
Text("Account")
}
.tag(0)
MacPreferencesView(preferences: model.preferences)
.tabItem {
Image(systemName: "gear")
Text("Preferences")
}
.tag(1)
- MacUpdatesView()
+ MacUpdatesView(updaterViewModel: updaterViewModel)
.tabItem {
Image(systemName: "arrow.down.circle")
Text("Updates")
}
.tag(2)
}
.frame(minWidth: 500, maxWidth: 500, minHeight: 200)
.padding()
// .preferredColorScheme(preferences.selectedColorScheme) // See PreferencesModel for info.
}
#endif
}
private func showLastDraftOrCreateNewLocalPost() {
if model.editor.lastDraftURL != nil {
self.model.selectedPost = model.editor.fetchLastDraftFromAppStorage()
} else {
createNewLocalPost()
}
}
private func createNewLocalPost() {
withAnimation {
// Un-set the currently selected post
self.model.selectedPost = nil
}
// Create the new-post managed object
let managedPost = model.editor.generateNewLocalPost(withFont: model.preferences.font)
withAnimation {
// Set it as the selectedPost
DispatchQueue.main.asyncAfter(deadline: .now()) {
self.model.selectedPost = managedPost
}
}
}
}
diff --git a/macOS/AppDelegate.swift b/macOS/AppDelegate.swift
index e002e64..410ffe9 100644
--- a/macOS/AppDelegate.swift
+++ b/macOS/AppDelegate.swift
@@ -1,61 +1,38 @@
import Cocoa
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
// 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)
}
}
// 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
})?.activate(options: .activateAllWindows)
}
}
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)
}
}
return true
}
}
diff --git a/macOS/Settings/MacUpdatesView.swift b/macOS/Settings/MacUpdatesView.swift
index 53b4f0e..afb6c48 100644
--- a/macOS/Settings/MacUpdatesView.swift
+++ b/macOS/Settings/MacUpdatesView.swift
@@ -1,95 +1,91 @@
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 = """
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 = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .short
formatter.doesRelativeDateFormatting = true
return formatter
}()
var body: some View {
VStack(spacing: 24) {
Toggle(isOn: $automaticallyChecksForUpdates, label: {
Text("Check for updates automatically")
})
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")
})
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
+ lastUpdateCheck = updaterViewModel.getLastUpdateCheckDate()
}
.onChange(of: automaticallyChecksForUpdates) { value in
- SUUpdater.shared()?.automaticallyChecksForUpdates = value
+ updaterViewModel.automaticallyCheckForUpdates = value
}
- .onChange(of: subscribeToBetaUpdates) { value in
- if value {
- SUUpdater.shared()?.feedURL = URL(string: AppcastFeedUrl.beta.rawValue)
- } else {
- SUUpdater.shared()?.feedURL = URL(string: AppcastFeedUrl.release.rawValue)
- }
+ .onChange(of: subscribeToBetaUpdates) { _ in
+ updaterViewModel.toggleAllowedChannels()
}
}
}
struct MacUpdatesView_Previews: PreviewProvider {
static var previews: some View {
- MacUpdatesView()
+ MacUpdatesView(updaterViewModel: MacUpdatesViewModel())
}
}
diff --git a/macOS/Settings/MacUpdatesViewModel.swift b/macOS/Settings/MacUpdatesViewModel.swift
index 6b0c6fa..b960524 100644
--- a/macOS/Settings/MacUpdatesViewModel.swift
+++ b/macOS/Settings/MacUpdatesViewModel.swift
@@ -1,38 +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: nil,
+ 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)
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 17, 7:23 PM (7 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3324284

Event Timeline