Merge pull request #3301 from stuartbreckenridge/theme-tweaks
Theme tweaks
This commit is contained in:
commit
37cb93ed1a
@ -107,6 +107,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
|||||||
private var softwareUpdater: SPUUpdater!
|
private var softwareUpdater: SPUUpdater!
|
||||||
private var crashReporter: PLCrashReporter!
|
private var crashReporter: PLCrashReporter!
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
private var themeImportPath: String?
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
NSWindow.allowsAutomaticWindowTabbing = false
|
NSWindow.allowsAutomaticWindowTabbing = false
|
||||||
@ -890,15 +892,11 @@ internal extension AppDelegate {
|
|||||||
} else {
|
} else {
|
||||||
importTheme()
|
importTheme()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error" : error])
|
NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error" : error, "path": filename])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func confirmImportSuccess(themeName: String) {
|
func confirmImportSuccess(themeName: String) {
|
||||||
@ -912,27 +910,37 @@ internal extension AppDelegate {
|
|||||||
alert.informativeText = NSString.localizedStringWithFormat(localizedInformativeText as NSString, themeName) as String
|
alert.informativeText = NSString.localizedStringWithFormat(localizedInformativeText as NSString, themeName) as String
|
||||||
|
|
||||||
alert.addButton(withTitle: NSLocalizedString("OK", comment: "OK"))
|
alert.addButton(withTitle: NSLocalizedString("OK", comment: "OK"))
|
||||||
|
|
||||||
alert.beginSheetModal(for: window)
|
alert.beginSheetModal(for: window)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func themeImportError(_ note: Notification) {
|
@objc func themeImportError(_ note: Notification) {
|
||||||
guard let userInfo = note.userInfo,
|
guard let userInfo = note.userInfo,
|
||||||
let error = userInfo["error"] as? Error,
|
let error = userInfo["error"] as? Error else {
|
||||||
let window = mainWindowController?.window else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
themeImportPath = userInfo["path"] as? String
|
||||||
var informativeText: String = ""
|
var informativeText: String = ""
|
||||||
if let decodingError = error as? DecodingError {
|
if let decodingError = error as? DecodingError {
|
||||||
switch decodingError {
|
switch decodingError {
|
||||||
case .typeMismatch(let type, _):
|
case .typeMismatch(let type, _):
|
||||||
informativeText = "Type '\(type)' mismatch."
|
let localizedError = NSLocalizedString("This theme cannot be used because the the type—“%@”—is mismatched in the Info.plist", comment: "Type mismatch")
|
||||||
|
informativeText = NSString.localizedStringWithFormat(localizedError as NSString, type as! CVarArg) as String
|
||||||
case .valueNotFound(let value, _):
|
case .valueNotFound(let value, _):
|
||||||
informativeText = "Value '\(value)' not found."
|
let localizedError = NSLocalizedString("This theme cannot be used because the the value—“%@”—is not found in the Info.plist.", comment: "Decoding value missing")
|
||||||
|
informativeText = NSString.localizedStringWithFormat(localizedError as NSString, value as! CVarArg) as String
|
||||||
case .keyNotFound(let codingKey, _):
|
case .keyNotFound(let codingKey, _):
|
||||||
informativeText = "Key '\(codingKey.stringValue)' not found."
|
let localizedError = NSLocalizedString("This theme cannot be used because the the key—“%@”—is not found in the Info.plist.", comment: "Decoding key missing")
|
||||||
case .dataCorrupted( _):
|
informativeText = NSString.localizedStringWithFormat(localizedError as NSString, codingKey.stringValue) as String
|
||||||
informativeText = error.localizedDescription
|
case .dataCorrupted(let context):
|
||||||
|
guard let error = context.underlyingError as NSError?,
|
||||||
|
let debugDescription = error.userInfo["NSDebugDescription"] as? String else {
|
||||||
|
informativeText = error.localizedDescription
|
||||||
|
break
|
||||||
|
}
|
||||||
|
let localizedError = NSLocalizedString("This theme cannot be used because of data corruption in the Info.plist: %@.", comment: "Decoding key missing")
|
||||||
|
informativeText = NSString.localizedStringWithFormat(localizedError as NSString, debugDescription) as String
|
||||||
|
|
||||||
default:
|
default:
|
||||||
informativeText = error.localizedDescription
|
informativeText = error.localizedDescription
|
||||||
}
|
}
|
||||||
@ -944,9 +952,26 @@ internal extension AppDelegate {
|
|||||||
let alert = NSAlert()
|
let alert = NSAlert()
|
||||||
alert.alertStyle = .warning
|
alert.alertStyle = .warning
|
||||||
alert.messageText = NSLocalizedString("Theme Error", comment: "Theme download error")
|
alert.messageText = NSLocalizedString("Theme Error", comment: "Theme download error")
|
||||||
alert.informativeText = NSLocalizedString("This theme cannot be imported due to the following error: \(informativeText)", comment: "Theme download error information")
|
alert.informativeText = informativeText
|
||||||
|
alert.addButton(withTitle: NSLocalizedString("Open Theme Folder", comment: "Open Theme Folder"))
|
||||||
alert.addButton(withTitle: NSLocalizedString("OK", comment: "OK"))
|
alert.addButton(withTitle: NSLocalizedString("OK", comment: "OK"))
|
||||||
alert.beginSheetModal(for: window)
|
|
||||||
|
let button = alert.buttons.first
|
||||||
|
button?.target = self
|
||||||
|
button?.action = #selector(self.openThemesFolder(_:))
|
||||||
|
alert.buttons[0].keyEquivalent = "\033"
|
||||||
|
alert.buttons[1].keyEquivalent = "\r"
|
||||||
|
alert.runModal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func openThemesFolder(_ sender: Any) {
|
||||||
|
if themeImportPath == nil {
|
||||||
|
let url = URL(fileURLWithPath: ArticleThemesManager.shared.folderPath)
|
||||||
|
NSWorkspace.shared.open(url)
|
||||||
|
} else {
|
||||||
|
let url = URL(fileURLWithPath: themeImportPath!)
|
||||||
|
NSWorkspace.shared.open(url.deletingLastPathComponent())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,6 @@ struct ArticleTheme: Equatable {
|
|||||||
let data = try Data(contentsOf: URL(fileURLWithPath: infoPath))
|
let data = try Data(contentsOf: URL(fileURLWithPath: infoPath))
|
||||||
self.info = try PropertyListDecoder().decode(ArticleThemePlist.self, from: data)
|
self.info = try PropertyListDecoder().decode(ArticleThemePlist.self, from: data)
|
||||||
|
|
||||||
|
|
||||||
let corePath = Bundle.main.path(forResource: "core", ofType: "css")!
|
let corePath = Bundle.main.path(forResource: "core", ofType: "css")!
|
||||||
let stylesheetPath = (path as NSString).appendingPathComponent("stylesheet.css")
|
let stylesheetPath = (path as NSString).appendingPathComponent("stylesheet.css")
|
||||||
if let stylesheetCSS = Self.stringAtPath(stylesheetPath) {
|
if let stylesheetCSS = Self.stringAtPath(stylesheetPath) {
|
||||||
|
@ -22,7 +22,6 @@ public class ArticleThemeDownloader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static let shared = ArticleThemeDownloader()
|
public static let shared = ArticleThemeDownloader()
|
||||||
private init() {}
|
private init() {}
|
||||||
|
|
||||||
|
@ -1,7 +1,31 @@
|
|||||||
# Themes
|
# Themes
|
||||||
|
|
||||||
## Add Themes Directly to NetNewsWire
|
## `.nnwtheme` Structure
|
||||||
Theme developers: on iOS and macOS, themes can be opened directly in NetNewsWire using the below URL scheme:
|
|
||||||
|
An `.nnwtheme` comprises of three files:
|
||||||
|
- `Info.plist`
|
||||||
|
- `template.html`
|
||||||
|
- `stylesheet.css`
|
||||||
|
|
||||||
|
### Info.plist
|
||||||
|
The `Info.plist` requires the following keys/types:
|
||||||
|
|
||||||
|
|Key|Type|Notes|
|
||||||
|
|---|---|---|
|
||||||
|
|`ThemeIdentifier`|`String`|Unique identifier for the theme, e.g. using reverse domain name.|
|
||||||
|
|`Name`|`String`|Theme name|
|
||||||
|
|`CreatorHomePage`|`String`||
|
||||||
|
|`CreatorName`|`String`||
|
||||||
|
|`Version`|`Integer`||
|
||||||
|
|
||||||
|
### template.html
|
||||||
|
This provides a starting point for editing the structure of the page. Theme variables are documented in the header.
|
||||||
|
|
||||||
|
### stylesheet.css
|
||||||
|
This provides a starting point for editing the style of the page.
|
||||||
|
|
||||||
|
## Add Themes Directly to NetNewsWire with URL Scheme
|
||||||
|
On iOS and macOS, themes can be opened directly in NetNewsWire using the below URL scheme:
|
||||||
|
|
||||||
`netnewswire://theme/add?url={url}`
|
`netnewswire://theme/add?url={url}`
|
||||||
|
|
||||||
|
@ -17,20 +17,34 @@ extension UIViewController {
|
|||||||
presentAccountError(accountError, dismiss: dismiss)
|
presentAccountError(accountError, dismiss: dismiss)
|
||||||
} else if let decodingError = error as? DecodingError {
|
} else if let decodingError = error as? DecodingError {
|
||||||
let errorTitle = NSLocalizedString("Error", comment: "Error")
|
let errorTitle = NSLocalizedString("Error", comment: "Error")
|
||||||
|
let infromativeText: String = ""
|
||||||
switch decodingError {
|
switch decodingError {
|
||||||
case .typeMismatch(let type, _):
|
case .typeMismatch(let type, _):
|
||||||
let str = "Type '\(type)' mismatch."
|
let localizedError = NSLocalizedString("This theme cannot be used because the the type—“%@”—is mismatched in the Info.plist", comment: "Type mismatch")
|
||||||
presentError(title: errorTitle, message: str, dismiss: dismiss)
|
informativeText = NSString.localizedStringWithFormat(localizedError as NSString, type as! CVarArg) as String
|
||||||
|
presentError(title: title, message: infromativeText, dismiss: dismiss)
|
||||||
case .valueNotFound(let value, _):
|
case .valueNotFound(let value, _):
|
||||||
let str = "Value '\(value)' not found."
|
let localizedError = NSLocalizedString("This theme cannot be used because the the value—“%@”—is not found in the Info.plist.", comment: "Decoding value missing")
|
||||||
presentError(title: errorTitle, message: str, dismiss: dismiss)
|
informativeText = NSString.localizedStringWithFormat(localizedError as NSString, value as! CVarArg) as String
|
||||||
|
presentError(title: title, message: infromativeText, dismiss: dismiss)
|
||||||
case .keyNotFound(let codingKey, _):
|
case .keyNotFound(let codingKey, _):
|
||||||
let str = "Key '\(codingKey.stringValue)' not found."
|
let localizedError = NSLocalizedString("This theme cannot be used because the the key—“%@”—is not found in the Info.plist.", comment: "Decoding key missing")
|
||||||
presentError(title: errorTitle, message: str, dismiss: dismiss)
|
informativeText = NSString.localizedStringWithFormat(localizedError as NSString, codingKey.stringValue) as String
|
||||||
case .dataCorrupted( _):
|
presentError(title: title, message: infromativeText, dismiss: dismiss)
|
||||||
presentError(title: errorTitle, message: error.localizedDescription, dismiss: dismiss)
|
case .dataCorrupted(let context):
|
||||||
|
guard let error = context.underlyingError as NSError?,
|
||||||
|
let debugDescription = error.userInfo["NSDebugDescription"] as? String else {
|
||||||
|
informativeText = error.localizedDescription
|
||||||
|
presentError(title: title, message: infromativeText, dismiss: dismiss)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let localizedError = NSLocalizedString("This theme cannot be used because of data corruption in the Info.plist. %@.", comment: "Decoding key missing")
|
||||||
|
informativeText = NSString.localizedStringWithFormat(localizedError as NSString, debugDescription) as String
|
||||||
|
presentError(title: title, message: infromativeText, dismiss: dismiss)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
presentError(title: errorTitle, message: error.localizedDescription, dismiss: dismiss)
|
informativeText = error.localizedDescription
|
||||||
|
presentError(title: title, message: infromativeText, dismiss: dismiss)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let errorTitle = NSLocalizedString("Error", comment: "Error")
|
let errorTitle = NSLocalizedString("Error", comment: "Error")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user