download themes using url scheme
this build enables iOS functionality only.
This commit is contained in:
parent
b43d8b6b28
commit
4fab4ffa7c
|
@ -112,6 +112,10 @@
|
|||
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 */; };
|
||||
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 */; };
|
||||
179D280F26F73D83003B2E0A /* ArticleThemePlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179D280C26F73D83003B2E0A /* ArticleThemePlist.swift */; };
|
||||
179DB1DFBCF9177104B12E0F /* AccountsNewsBlurWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */; };
|
||||
179DB3CE822BFCC2D774D9F4 /* AccountsNewsBlurWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */; };
|
||||
17A1597C24E3DEDD005DA32A /* RSCore in Frameworks */ = {isa = PBXBuildFile; productRef = 17A1597B24E3DEDD005DA32A /* RSCore */; };
|
||||
|
@ -1591,6 +1595,7 @@
|
|||
17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedView.swift; sourceTree = "<group>"; };
|
||||
1799E6A824C2F93F00511E91 /* InspectorPlatformModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorPlatformModifier.swift; sourceTree = "<group>"; };
|
||||
1799E6CC24C320D600511E91 /* InspectorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorModel.swift; sourceTree = "<group>"; };
|
||||
179D280C26F73D83003B2E0A /* ArticleThemePlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleThemePlist.swift; sourceTree = "<group>"; };
|
||||
179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsNewsBlurWindowController.swift; sourceTree = "<group>"; };
|
||||
17B223DB24AC24D2001E4592 /* TimelineLayoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineLayoutView.swift; sourceTree = "<group>"; };
|
||||
17D0682B2564F47E00C0B37E /* Localizable.stringsdict */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; path = Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
|
@ -2287,6 +2292,7 @@
|
|||
files = (
|
||||
5138E94924D3416D00AFF0FE /* RSCore in Frameworks */,
|
||||
5138E95824D3419000AFF0FE /* RSWeb in Frameworks */,
|
||||
179D280B26F6F93D003B2E0A /* Zip in Frameworks */,
|
||||
516B695F24D2F33B00B5702F /* Account in Frameworks */,
|
||||
5138E95224D3418100AFF0FE /* RSParser in Frameworks */,
|
||||
5138E94C24D3417A00AFF0FE /* RSDatabase in Frameworks */,
|
||||
|
@ -3446,6 +3452,7 @@
|
|||
children = (
|
||||
849A97871ED9ECEF007D329B /* ArticleTheme.swift */,
|
||||
849A97881ED9ECEF007D329B /* ArticleThemesManager.swift */,
|
||||
179D280C26F73D83003B2E0A /* ArticleThemePlist.swift */,
|
||||
);
|
||||
name = "Article Styles";
|
||||
path = Shared/ArticleStyles;
|
||||
|
@ -4143,6 +4150,7 @@
|
|||
513F32732593EE6F0003048F /* ArticlesDatabase */,
|
||||
513F32762593EE6F0003048F /* Secrets */,
|
||||
513F32792593EE6F0003048F /* SyncDatabase */,
|
||||
179D280A26F6F93D003B2E0A /* Zip */,
|
||||
);
|
||||
productName = "NetNewsWire-iOS";
|
||||
productReference = 840D617C2029031C009BC708 /* NetNewsWire.app */;
|
||||
|
@ -4315,6 +4323,7 @@
|
|||
51B0DF2324D2C7FA000AD99E /* XCRemoteSwiftPackageReference "RSParser" */,
|
||||
17192AD82567B3D500AAEACA /* XCRemoteSwiftPackageReference "Sparkle-Binary" */,
|
||||
519CA8E325841DB700EB079A /* XCRemoteSwiftPackageReference "plcrashreporter" */,
|
||||
179D280926F6F93D003B2E0A /* XCRemoteSwiftPackageReference "Zip" */,
|
||||
);
|
||||
productRefGroup = 849C64611ED37A5D003D8FC0 /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -5058,6 +5067,7 @@
|
|||
51E4991924A8090A00B667CB /* CacheCleaner.swift in Sources */,
|
||||
51E498F724A8085D00B667CB /* SearchTimelineFeedDelegate.swift in Sources */,
|
||||
175942AA24AD533200585066 /* RefreshInterval.swift in Sources */,
|
||||
179D280E26F73D83003B2E0A /* ArticleThemePlist.swift in Sources */,
|
||||
51E4993524A867E800B667CB /* AppNotifications.swift in Sources */,
|
||||
6535ECFC2680F9FF00C01CB5 /* IconImageCache.swift in Sources */,
|
||||
51C0515E24A77DF800194D5E /* MainApp.swift in Sources */,
|
||||
|
@ -5169,6 +5179,7 @@
|
|||
51E499D924A912C200B667CB /* SceneModel.swift in Sources */,
|
||||
51919FB424AAB97900541E64 /* FeedIconImageLoader.swift in Sources */,
|
||||
1769E32524BC5A65000E1E8E /* AddAccountView.swift in Sources */,
|
||||
179D280F26F73D83003B2E0A /* ArticleThemePlist.swift in Sources */,
|
||||
51E4994A24A8734C00B667CB /* ExtensionPointManager.swift in Sources */,
|
||||
514E6C0324AD29A300AC6F6E /* TimelineItemStatusView.swift in Sources */,
|
||||
1724126A257BBEBB00ACCEBC /* AddFeedbinViewModel.swift in Sources */,
|
||||
|
@ -5474,6 +5485,7 @@
|
|||
5186A635235EF3A800C97195 /* VibrantLabel.swift in Sources */,
|
||||
51F85BF92274AA7B00C787DC /* UIBarButtonItem-Extensions.swift in Sources */,
|
||||
51B62E68233186730085F949 /* IconView.swift in Sources */,
|
||||
179D280D26F73D83003B2E0A /* ArticleThemePlist.swift in Sources */,
|
||||
51C45296226509D300C03939 /* OPMLExporter.swift in Sources */,
|
||||
51C45291226509C800C03939 /* SmartFeed.swift in Sources */,
|
||||
51C452A722650A3D00C03939 /* RSImage-Extensions.swift in Sources */,
|
||||
|
@ -6399,6 +6411,14 @@
|
|||
minimumVersion = 2.0.0;
|
||||
};
|
||||
};
|
||||
179D280926F6F93D003B2E0A /* XCRemoteSwiftPackageReference "Zip" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/marmelroy/Zip.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 2.0.0;
|
||||
};
|
||||
};
|
||||
5102AE4324D17E820050839C /* XCRemoteSwiftPackageReference "RSCore" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/Ranchero-Software/RSCore.git";
|
||||
|
@ -6498,6 +6518,11 @@
|
|||
package = 5102AE4324D17E820050839C /* XCRemoteSwiftPackageReference "RSCore" */;
|
||||
productName = RSCoreResources;
|
||||
};
|
||||
179D280A26F6F93D003B2E0A /* Zip */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 179D280926F6F93D003B2E0A /* XCRemoteSwiftPackageReference "Zip" */;
|
||||
productName = Zip;
|
||||
};
|
||||
17A1597B24E3DEDD005DA32A /* RSCore */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 5102AE4324D17E820050839C /* XCRemoteSwiftPackageReference "RSCore" */;
|
||||
|
|
|
@ -117,6 +117,15 @@
|
|||
"revision": "9483a5d459b45c3ffd059f7b55f9638e268632fd",
|
||||
"version": "1.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Zip",
|
||||
"repositoryURL": "https://github.com/marmelroy/Zip.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "bd19d974e8a38cc8d3a88c90c8a107386c3b8ccf",
|
||||
"version": "2.1.1"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// ArticleThemePlist.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Stuart Breckenridge on 19/09/2021.
|
||||
// Copyright © 2021 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct ArticleThemePlist: Codable {
|
||||
public var name: String
|
||||
public var themeIdentifier: String
|
||||
public var creatorHomePage: String
|
||||
public var creatorName: String
|
||||
public var version: Int
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case name = "Name"
|
||||
case themeIdentifier = "ThemeIdentifer" // FIXME: Spelling error!
|
||||
case creatorHomePage = "CreatorHomePage"
|
||||
case creatorName = "CreatorName"
|
||||
case version = "Version"
|
||||
}
|
||||
}
|
|
@ -9,15 +9,16 @@
|
|||
import UIKit
|
||||
import UserNotifications
|
||||
import Account
|
||||
import Zip
|
||||
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate, URLSessionDownloadDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
var window: UIWindow?
|
||||
var coordinator = SceneCoordinator()
|
||||
|
||||
// UIWindowScene delegate
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
// UIWindowScene delegate
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
|
||||
window = UIWindow(windowScene: scene as! UIWindowScene)
|
||||
window!.tintColor = AppAssets.primaryAccentColor
|
||||
|
@ -46,12 +47,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
return
|
||||
}
|
||||
|
||||
if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity {
|
||||
if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity {
|
||||
coordinator.handle(userActivity)
|
||||
}
|
||||
|
||||
window!.makeKeyAndVisible()
|
||||
}
|
||||
}
|
||||
|
||||
func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
|
||||
appDelegate.resumeDatabaseProcessingIfNecessary()
|
||||
|
@ -79,9 +80,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
coordinator.resetFocus()
|
||||
}
|
||||
|
||||
func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
|
||||
func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
|
||||
return coordinator.stateRestorationActivity
|
||||
}
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
|
@ -89,7 +90,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
appDelegate.resumeDatabaseProcessingIfNecessary()
|
||||
coordinator.handle(response)
|
||||
}
|
||||
|
||||
|
||||
func suspend() {
|
||||
coordinator.suspend()
|
||||
}
|
||||
|
@ -165,8 +166,69 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
let filename = context.url.standardizedFileURL.path
|
||||
if filename.hasSuffix(ArticleTheme.nnwThemeSuffix) {
|
||||
self.coordinator.importTheme(filename: filename)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle theme URLs: netnewswire://theme/add?url={url}
|
||||
guard let comps = URLComponents(url: context.url, resolvingAgainstBaseURL: false),
|
||||
"theme" == comps.host,
|
||||
let queryItems = comps.queryItems else {
|
||||
return
|
||||
}
|
||||
|
||||
if let providedThemeURL = queryItems.first(where: { $0.name == "url" })?.value {
|
||||
if let themeURL = URL(string: providedThemeURL) {
|
||||
let request = URLRequest(url: themeURL)
|
||||
let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
|
||||
let downloadTask = session.downloadTask(with: request)
|
||||
downloadTask.resume()
|
||||
} else {
|
||||
print("No theme URL")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - URLSessionDownloadDelegate
|
||||
|
||||
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
|
||||
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)")
|
||||
|
||||
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.coordinator.importTheme(filename: renamedUnzippedDir.path)
|
||||
}
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue