From ea6e5b8434b7b7cac8cce9af1b6db02d4b5ec46c Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 7 Feb 2022 16:23:08 -0800 Subject: [PATCH 1/3] Change to no longer copy app distributed themes to the Themes folder. Fixes #3447 --- Mac/AppDelegate.swift | 2 +- Shared/ArticleStyles/ArticleTheme.swift | 7 ++- .../ArticleStyles/ArticleThemesManager.swift | 51 +++++++++---------- iOS/Settings/ArticleThemeImporter.swift | 2 +- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index cd9ea9429..42c8d9729 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -817,7 +817,7 @@ internal extension AppDelegate { guard let window = mainWindowController?.window else { return } do { - let theme = try ArticleTheme(path: filename) + let theme = try ArticleTheme(path: filename, isAppTheme: false) let alert = NSAlert() alert.alertStyle = .informational diff --git a/Shared/ArticleStyles/ArticleTheme.swift b/Shared/ArticleStyles/ArticleTheme.swift index e40bde46f..302c45a53 100644 --- a/Shared/ArticleStyles/ArticleTheme.swift +++ b/Shared/ArticleStyles/ArticleTheme.swift @@ -19,6 +19,7 @@ struct ArticleTheme: Equatable { let path: String? let template: String? let css: String? + let isAppTheme: Bool var name: String { guard let path = path else { return Self.defaultThemeName } @@ -49,9 +50,11 @@ struct ArticleTheme: Equatable { let templatePath = Bundle.main.path(forResource: "template", ofType: "html")! template = Self.stringAtPath(templatePath)! + + isAppTheme = true } - init(path: String) throws { + init(path: String, isAppTheme: Bool) throws { self.path = path let infoPath = (path as NSString).appendingPathComponent("Info.plist") @@ -68,6 +71,8 @@ struct ArticleTheme: Equatable { let templatePath = (path as NSString).appendingPathComponent("template.html") self.template = Self.stringAtPath(templatePath) + + self.isAppTheme = isAppTheme } static func stringAtPath(_ f: String) -> String? { diff --git a/Shared/ArticleStyles/ArticleThemesManager.swift b/Shared/ArticleStyles/ArticleThemesManager.swift index e2205240a..c23f1d7ae 100644 --- a/Shared/ArticleStyles/ArticleThemesManager.swift +++ b/Shared/ArticleStyles/ArticleThemesManager.swift @@ -48,6 +48,10 @@ final class ArticleThemesManager: NSObject, NSFilePresenter { } } + var themes: [ArticleTheme] { + return themeNames.compactMap { articleThemeWithThemeName($0) } + } + init(folderPath: String) { self.folderPath = folderPath self.currentTheme = ArticleTheme.defaultTheme @@ -61,15 +65,6 @@ final class ArticleThemesManager: NSObject, NSFilePresenter { abort() } - let themeFilenames = Bundle.main.paths(forResourcesOfType: ArticleTheme.nnwThemeSuffix, inDirectory: nil) - let installedThemes = readInstalledThemes() ?? [String: Date]() - for themeFilename in themeFilenames { - let themeName = ArticleTheme.themeNameForPath(themeFilename) - if !installedThemes.keys.contains(themeName) { - try? importTheme(filename: themeFilename) - } - } - updateThemeNames() updateCurrentTheme() @@ -98,11 +93,6 @@ final class ArticleThemesManager: NSObject, NSFilePresenter { } try FileManager.default.copyItem(atPath: filename, toPath: toFilename) - - let themeName = ArticleTheme.themeNameForPath(filename) - var installedThemes = readInstalledThemes() ?? [String: Date]() - installedThemes[themeName] = Date() - writeInstalledThemes(installedThemes) } func deleteTheme(themeName: String) { @@ -118,8 +108,14 @@ final class ArticleThemesManager: NSObject, NSFilePresenter { private extension ArticleThemesManager { func updateThemeNames() { - let updatedThemeNames = allThemePaths(folderPath).map { ArticleTheme.themeNameForPath($0) } - let sortedThemeNames = updatedThemeNames.sorted(by: { $0.compare($1, options: .caseInsensitive) == .orderedAscending }) + let appThemeFilenames = Bundle.main.paths(forResourcesOfType: ArticleTheme.nnwThemeSuffix, inDirectory: nil) + let appThemeNames = Set(appThemeFilenames.map { ArticleTheme.themeNameForPath($0) }) + + let installedThemeNames = Set(allThemePaths(folderPath).map { ArticleTheme.themeNameForPath($0) }) + + let allThemeNames = appThemeNames.union(installedThemeNames) + + let sortedThemeNames = allThemeNames.sorted(by: { $0.compare($1, options: .caseInsensitive) == .orderedAscending }) if sortedThemeNames != themeNames { themeNames = sortedThemeNames } @@ -130,11 +126,20 @@ private extension ArticleThemesManager { return ArticleTheme.defaultTheme } - guard let path = pathForThemeName(themeName, folder: folderPath) else { + let path: String + let isAppTheme: Bool + if let appThemePath = Bundle.main.url(forResource: themeName, withExtension: ArticleTheme.nnwThemeSuffix)?.path { + path = appThemePath + isAppTheme = true + } else if let installedPath = pathForThemeName(themeName, folder: folderPath) { + path = installedPath + isAppTheme = false + } else { return nil } + do { - return try ArticleTheme(path: path) + return try ArticleTheme(path: path, isAppTheme: isAppTheme) } catch { NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error": error]) return nil @@ -178,14 +183,4 @@ private extension ArticleThemesManager { return nil } - func readInstalledThemes() -> [String: Date]? { - let filePath = (folderPath as NSString).appendingPathComponent("InstalledThemes.plist") - return NSDictionary(contentsOfFile: filePath) as? [String: Date] - } - - func writeInstalledThemes(_ dict: [String: Date]) { - let filePath = (folderPath as NSString).appendingPathComponent("InstalledThemes.plist") - (dict as NSDictionary).write(toFile: filePath, atomically: true) - } - } diff --git a/iOS/Settings/ArticleThemeImporter.swift b/iOS/Settings/ArticleThemeImporter.swift index 135d19989..4a6996831 100644 --- a/iOS/Settings/ArticleThemeImporter.swift +++ b/iOS/Settings/ArticleThemeImporter.swift @@ -11,7 +11,7 @@ import UIKit struct ArticleThemeImporter { static func importTheme(controller: UIViewController, filename: String) throws { - let theme = try ArticleTheme(path: filename) + let theme = try ArticleTheme(path: filename, isAppTheme: false) let localizedTitleText = NSLocalizedString("Install theme “%@” by %@?", comment: "Theme message text") let title = NSString.localizedStringWithFormat(localizedTitleText as NSString, theme.name, theme.creatorName) as String From a1bfa4afdce66bd3e7c8d24a8342ab5b9808257d Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 7 Feb 2022 16:27:10 -0800 Subject: [PATCH 2/3] Make sure that the theme names are always up to date before setting the current theme --- Shared/ArticleStyles/ArticleThemesManager.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Shared/ArticleStyles/ArticleThemesManager.swift b/Shared/ArticleStyles/ArticleThemesManager.swift index c23f1d7ae..a8c7fe372 100644 --- a/Shared/ArticleStyles/ArticleThemesManager.swift +++ b/Shared/ArticleStyles/ArticleThemesManager.swift @@ -31,6 +31,7 @@ final class ArticleThemesManager: NSObject, NSFilePresenter { set { if newValue != currentThemeName { AppDefaults.shared.currentThemeName = newValue + updateThemeNames() updateCurrentTheme() } } From 0c183f4bdfb21403d12e65ef456e62fbbf39db34 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 7 Feb 2022 16:41:00 -0800 Subject: [PATCH 3/3] Prevent app themes from attempting to be deleted --- .../ArticleStyles/ArticleThemesManager.swift | 56 +++++++++---------- .../ArticleThemesTableViewController.swift | 7 ++- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/Shared/ArticleStyles/ArticleThemesManager.swift b/Shared/ArticleStyles/ArticleThemesManager.swift index a8c7fe372..ff0365e9f 100644 --- a/Shared/ArticleStyles/ArticleThemesManager.swift +++ b/Shared/ArticleStyles/ArticleThemesManager.swift @@ -49,10 +49,6 @@ final class ArticleThemesManager: NSObject, NSFilePresenter { } } - var themes: [ArticleTheme] { - return themeNames.compactMap { articleThemeWithThemeName($0) } - } - init(folderPath: String) { self.folderPath = folderPath self.currentTheme = ArticleTheme.defaultTheme @@ -96,32 +92,6 @@ final class ArticleThemesManager: NSObject, NSFilePresenter { try FileManager.default.copyItem(atPath: filename, toPath: toFilename) } - func deleteTheme(themeName: String) { - if let filename = pathForThemeName(themeName, folder: folderPath) { - try? FileManager.default.removeItem(atPath: filename) - } - } - -} - -// MARK : Private - -private extension ArticleThemesManager { - - func updateThemeNames() { - let appThemeFilenames = Bundle.main.paths(forResourcesOfType: ArticleTheme.nnwThemeSuffix, inDirectory: nil) - let appThemeNames = Set(appThemeFilenames.map { ArticleTheme.themeNameForPath($0) }) - - let installedThemeNames = Set(allThemePaths(folderPath).map { ArticleTheme.themeNameForPath($0) }) - - let allThemeNames = appThemeNames.union(installedThemeNames) - - let sortedThemeNames = allThemeNames.sorted(by: { $0.compare($1, options: .caseInsensitive) == .orderedAscending }) - if sortedThemeNames != themeNames { - themeNames = sortedThemeNames - } - } - func articleThemeWithThemeName(_ themeName: String) -> ArticleTheme? { if themeName == AppDefaults.defaultThemeName { return ArticleTheme.defaultTheme @@ -148,6 +118,32 @@ private extension ArticleThemesManager { } + func deleteTheme(themeName: String) { + if let filename = pathForThemeName(themeName, folder: folderPath) { + try? FileManager.default.removeItem(atPath: filename) + } + } + +} + +// MARK : Private + +private extension ArticleThemesManager { + + func updateThemeNames() { + let appThemeFilenames = Bundle.main.paths(forResourcesOfType: ArticleTheme.nnwThemeSuffix, inDirectory: nil) + let appThemeNames = Set(appThemeFilenames.map { ArticleTheme.themeNameForPath($0) }) + + let installedThemeNames = Set(allThemePaths(folderPath).map { ArticleTheme.themeNameForPath($0) }) + + let allThemeNames = appThemeNames.union(installedThemeNames) + + let sortedThemeNames = allThemeNames.sorted(by: { $0.compare($1, options: .caseInsensitive) == .orderedAscending }) + if sortedThemeNames != themeNames { + themeNames = sortedThemeNames + } + } + func defaultArticleTheme() -> ArticleTheme { return articleThemeWithThemeName(AppDefaults.defaultThemeName)! } diff --git a/iOS/Settings/ArticleThemesTableViewController.swift b/iOS/Settings/ArticleThemesTableViewController.swift index 26e28944f..b7793d5c4 100644 --- a/iOS/Settings/ArticleThemesTableViewController.swift +++ b/iOS/Settings/ArticleThemesTableViewController.swift @@ -70,9 +70,10 @@ class ArticleThemesTableViewController: UITableViewController { } override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { - guard indexPath.row != 0, - let cell = tableView.cellForRow(at: indexPath), - let themeName = cell.textLabel?.text else { return nil } + guard let cell = tableView.cellForRow(at: indexPath), + let themeName = cell.textLabel?.text, + let theme = ArticleThemesManager.shared.articleThemeWithThemeName(themeName), + !theme.isAppTheme else { return nil } let deleteTitle = NSLocalizedString("Delete", comment: "Delete") let deleteAction = UIContextualAction(style: .normal, title: deleteTitle) { [weak self] (action, view, completion) in