diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index d56800522..afdbbfd56 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -765,7 +765,7 @@ extension AppDelegate { } -private extension AppDelegate { +internal extension AppDelegate { func fireOldTimers() { // It’s possible there’s a refresh timer set to go off in the past. diff --git a/Mac/Resources/Info.plist b/Mac/Resources/Info.plist index ce803e9ed..be2d2bf9e 100644 --- a/Mac/Resources/Info.plist +++ b/Mac/Resources/Info.plist @@ -31,6 +31,7 @@ RSS Feed CFBundleURLSchemes + netnewswire feed feeds x-netnewswire-feed diff --git a/Mac/Scriptability/AppDelegate+Scriptability.swift b/Mac/Scriptability/AppDelegate+Scriptability.swift index 2b6b6f5db..81eba91c2 100644 --- a/Mac/Scriptability/AppDelegate+Scriptability.swift +++ b/Mac/Scriptability/AppDelegate+Scriptability.swift @@ -18,6 +18,7 @@ import Foundation import Articles +import Zip protocol AppDelegateAppleEvents { func installAppleEventHandlers() @@ -44,6 +45,63 @@ extension AppDelegate : AppDelegateAppleEvents { return } + // Handle themes + if urlString.hasPrefix("netnewswire://theme") { + guard let comps = URLComponents(string: urlString), + let queryItems = comps.queryItems, + let themeURLString = queryItems.first(where: { $0.name == "url" })?.value else { + return + } + + if let themeURL = URL(string: themeURLString) { + let request = URLRequest(url: themeURL) + let task = URLSession.shared.downloadTask(with: request) { location, response, error in + var downloadDirectory = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first! + try? FileManager.default.createDirectory(at: downloadDirectory, withIntermediateDirectories: true, attributes: nil) + let tmpFileName = UUID().uuidString + ".zip" + downloadDirectory.appendPathComponent("\(tmpFileName)") + + if location == nil { + return + } + + do { + try FileManager.default.moveItem(at: location!, to: downloadDirectory) + + var unzippedDir = downloadDirectory + unzippedDir = unzippedDir.deletingLastPathComponent() + unzippedDir.appendPathComponent("newtheme.nnwtheme") + + try Zip.unzipFile(downloadDirectory, destination: unzippedDir, overwrite: true, password: nil, progress: nil, fileOutputHandler: nil) + try FileManager.default.removeItem(at: downloadDirectory) + + let decoder = PropertyListDecoder() + let plistURL = URL(fileURLWithPath: unzippedDir.appendingPathComponent("Info.plist").path) + + let data = try Data(contentsOf: plistURL) + let plist = try decoder.decode(ArticleThemePlist.self, from: data) + + // rename + var renamedUnzippedDir = unzippedDir.deletingLastPathComponent() + renamedUnzippedDir.appendPathComponent(plist.name + ".nnwtheme") + if FileManager.default.fileExists(atPath: renamedUnzippedDir.path) { + try FileManager.default.removeItem(at: renamedUnzippedDir) + } + try FileManager.default.moveItem(at: unzippedDir, to: renamedUnzippedDir) + DispatchQueue.main.async { + self.importTheme(filename: renamedUnzippedDir.path) + } + } catch { + print(error) + } + } + task.resume() + } + return + + } + + // Special case URL with specific scheme handler x-netnewswire-feed: intended to ensure we open // it regardless of which news reader may be set as the default let nnwScheme = "x-netnewswire-feed:" diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index eb9d1aa9b..1e1787b22 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -112,6 +112,8 @@ 1799E6AA24C2F93F00511E91 /* InspectorPlatformModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1799E6A824C2F93F00511E91 /* InspectorPlatformModifier.swift */; }; 1799E6CD24C320D600511E91 /* InspectorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1799E6CC24C320D600511E91 /* InspectorModel.swift */; }; 1799E6CE24C320D600511E91 /* InspectorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1799E6CC24C320D600511E91 /* InspectorModel.swift */; }; + 179C39EA26F76B0500D4E741 /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = 179C39E926F76B0500D4E741 /* Zip */; }; + 179C39EB26F76B3800D4E741 /* ArticleThemePlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179D280C26F73D83003B2E0A /* ArticleThemePlist.swift */; }; 179D280B26F6F93D003B2E0A /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = 179D280A26F6F93D003B2E0A /* Zip */; }; 179D280D26F73D83003B2E0A /* ArticleThemePlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179D280C26F73D83003B2E0A /* ArticleThemePlist.swift */; }; 179D280E26F73D83003B2E0A /* ArticleThemePlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179D280C26F73D83003B2E0A /* ArticleThemePlist.swift */; }; @@ -2319,6 +2321,7 @@ 51C4CFF624D37DD500AF9874 /* Secrets in Frameworks */, 51A737AE24DB19730015FA66 /* RSCore in Frameworks */, 51A737C824DB19CC0015FA66 /* RSParser in Frameworks */, + 179C39EA26F76B0500D4E741 /* Zip in Frameworks */, 51E4DAED2425F6940091EB5B /* CloudKit.framework in Frameworks */, 514C16E124D2EF38009A3AFA /* RSCoreResources in Frameworks */, 514C16CE24D2E63F009A3AFA /* Account in Frameworks */, @@ -4192,6 +4195,7 @@ 5132775D2590FC640064F1E7 /* Articles */, 513277602590FC640064F1E7 /* ArticlesDatabase */, 513277632590FC640064F1E7 /* SyncDatabase */, + 179C39E926F76B0500D4E741 /* Zip */, ); productName = NetNewsWire; productReference = 849C64601ED37A5D003D8FC0 /* NetNewsWire.app */; @@ -5815,6 +5819,7 @@ 510C418124E5D1AE008226FD /* ExtensionContainers.swift in Sources */, 51EC114C2149FE3300B296E3 /* FolderTreeMenu.swift in Sources */, 849ADEE42359817E000E1B81 /* NNW3ImportController.swift in Sources */, + 179C39EB26F76B3800D4E741 /* ArticleThemePlist.swift in Sources */, 849A97A31ED9F180007D329B /* FolderTreeControllerDelegate.swift in Sources */, 51126DA4225FDE2F00722696 /* RSImage-Extensions.swift in Sources */, 510C43F7243D035C009F70C3 /* ExtensionPoint.swift in Sources */, @@ -6518,6 +6523,11 @@ package = 5102AE4324D17E820050839C /* XCRemoteSwiftPackageReference "RSCore" */; productName = RSCoreResources; }; + 179C39E926F76B0500D4E741 /* Zip */ = { + isa = XCSwiftPackageProductDependency; + package = 179D280926F6F93D003B2E0A /* XCRemoteSwiftPackageReference "Zip" */; + productName = Zip; + }; 179D280A26F6F93D003B2E0A /* Zip */ = { isa = XCSwiftPackageProductDependency; package = 179D280926F6F93D003B2E0A /* XCRemoteSwiftPackageReference "Zip" */;