Tidy up on the themes view

This commit is contained in:
Stuart Breckenridge
2022-12-20 22:04:04 +08:00
parent 53e49aa699
commit fe5a7d185b
14 changed files with 77 additions and 1642 deletions

View File

@@ -96,7 +96,6 @@
510C418624E5D1B4008226FD /* ExtensionFeedAddRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C87A23F2317700032075 /* ExtensionFeedAddRequest.swift */; };
510C43F7243D035C009F70C3 /* ExtensionPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510C43F6243D035C009F70C3 /* ExtensionPoint.swift */; };
510C43F8243D035C009F70C3 /* ExtensionPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510C43F6243D035C009F70C3 /* ExtensionPoint.swift */; };
510FFAB326EEA22C00F32265 /* ArticleThemesTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510FFAB226EEA22C00F32265 /* ArticleThemesTableViewController.swift */; };
51102165233A7D6C0007A5F7 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */; };
51107746243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51107745243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift */; };
51107747243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51107745243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift */; };
@@ -232,7 +231,6 @@
51627A6723861DA3007B3B4B /* MasterFeedViewController+Drag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A6623861DA3007B3B4B /* MasterFeedViewController+Drag.swift */; };
51627A6923861DED007B3B4B /* MasterFeedViewController+Drop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A6823861DED007B3B4B /* MasterFeedViewController+Drop.swift */; };
51627A93238A3836007B3B4B /* CroppingPreviewParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A92238A3836007B3B4B /* CroppingPreviewParameters.swift */; };
516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */; };
516AE9B32371C372007DEEAA /* MasterFeedTableViewSectionHeaderLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516AE9B22371C372007DEEAA /* MasterFeedTableViewSectionHeaderLayout.swift */; };
516AE9DF2372269A007DEEAA /* IconImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516AE9DE2372269A007DEEAA /* IconImage.swift */; };
516AE9E02372269A007DEEAA /* IconImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516AE9DE2372269A007DEEAA /* IconImage.swift */; };
@@ -280,7 +278,6 @@
519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; };
51A052CE244FB9D7006C2024 /* AddFeedWIndowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A052CD244FB9D6006C2024 /* AddFeedWIndowController.swift */; };
51A052CF244FB9D7006C2024 /* AddFeedWIndowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A052CD244FB9D6006C2024 /* AddFeedWIndowController.swift */; };
51A1699A235E10D700EB091F /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51A16990235E10D600EB091F /* Settings.storyboard */; };
51A66685238075AE00CB272D /* AddWebFeedDefaultContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A66684238075AE00CB272D /* AddWebFeedDefaultContainer.swift */; };
51A737AE24DB19730015FA66 /* RSCore in Frameworks */ = {isa = PBXBuildFile; productRef = 51A737AD24DB19730015FA66 /* RSCore */; };
51A737AF24DB19730015FA66 /* RSCore in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 51A737AD24DB19730015FA66 /* RSCore */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
@@ -839,8 +836,6 @@
DF28B453294FE6C600C4D8CA /* EnableExtensionPointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF28B452294FE6C600C4D8CA /* EnableExtensionPointView.swift */; };
DF28B455294FE74A00C4D8CA /* ExtensionSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF28B454294FE74A00C4D8CA /* ExtensionSectionHeader.swift */; };
DF28B4572950163F00C4D8CA /* EnableExtensionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF28B4562950163F00C4D8CA /* EnableExtensionViewModel.swift */; };
DF32ABE829493193008E3A12 /* SettingsComboTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF32ABE629493192008E3A12 /* SettingsComboTableViewCell.swift */; };
DF32ABE929493193008E3A12 /* SettingsComboTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DF32ABE729493193008E3A12 /* SettingsComboTableViewCell.xib */; };
DF32ABEB29494CF1008E3A12 /* Settings.strings in Resources */ = {isa = PBXBuildFile; fileRef = DF32ABEA29494CF0008E3A12 /* Settings.strings */; };
DF3630EB2936183D00326FB8 /* OPMLDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3630EA2936183D00326FB8 /* OPMLDocument.swift */; };
DF3630EC2936183D00326FB8 /* OPMLDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3630EA2936183D00326FB8 /* OPMLDocument.swift */; };
@@ -1210,7 +1205,6 @@
510C416624E5CDE3008226FD /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; sourceTree = "<group>"; };
510C418724E5D2E3008226FD /* NetNewsWire_shareextension_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_shareextension_target.xcconfig; sourceTree = "<group>"; };
510C43F6243D035C009F70C3 /* ExtensionPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPoint.swift; sourceTree = "<group>"; };
510FFAB226EEA22C00F32265 /* ArticleThemesTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleThemesTableViewController.swift; sourceTree = "<group>"; };
51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleExtractorButton.swift; sourceTree = "<group>"; };
51107745243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPointPreferencesViewController.swift; sourceTree = "<group>"; };
51121AA12265430A00BC0EC1 /* NetNewsWire_iOSapp_target.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSapp_target.xcconfig; sourceTree = "<group>"; };
@@ -1273,7 +1267,6 @@
51627A6623861DA3007B3B4B /* MasterFeedViewController+Drag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MasterFeedViewController+Drag.swift"; sourceTree = "<group>"; };
51627A6823861DED007B3B4B /* MasterFeedViewController+Drop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MasterFeedViewController+Drop.swift"; sourceTree = "<group>"; };
51627A92238A3836007B3B4B /* CroppingPreviewParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CroppingPreviewParameters.swift; sourceTree = "<group>"; };
516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsTableViewCell.xib; sourceTree = "<group>"; };
516AE5FF246AF34100731738 /* RedditAdd.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = RedditAdd.storyboard; sourceTree = "<group>"; };
516AE601246AF36100731738 /* RedditSelectTypeTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedditSelectTypeTableViewController.swift; sourceTree = "<group>"; };
516AE603246AF37B00731738 /* RedditSelectAccountTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedditSelectAccountTableViewController.swift; sourceTree = "<group>"; };
@@ -1308,7 +1301,6 @@
519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = "<group>"; };
519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
51A052CD244FB9D6006C2024 /* AddFeedWIndowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AddFeedWIndowController.swift; path = AddFeed/AddFeedWIndowController.swift; sourceTree = "<group>"; };
51A16990235E10D600EB091F /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = "<group>"; };
51A66684238075AE00CB272D /* AddWebFeedDefaultContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedDefaultContainer.swift; sourceTree = "<group>"; };
51A9A5E32380C8870033AADF /* ShareFolderPickerAccountCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShareFolderPickerAccountCell.xib; sourceTree = "<group>"; };
51A9A5E52380C8B20033AADF /* ShareFolderPickerFolderCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShareFolderPickerFolderCell.xib; sourceTree = "<group>"; };
@@ -1610,8 +1602,6 @@
DF28B452294FE6C600C4D8CA /* EnableExtensionPointView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionPointView.swift; sourceTree = "<group>"; };
DF28B454294FE74A00C4D8CA /* ExtensionSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionSectionHeader.swift; sourceTree = "<group>"; };
DF28B4562950163F00C4D8CA /* EnableExtensionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionViewModel.swift; sourceTree = "<group>"; };
DF32ABE629493192008E3A12 /* SettingsComboTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsComboTableViewCell.swift; sourceTree = "<group>"; };
DF32ABE729493193008E3A12 /* SettingsComboTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsComboTableViewCell.xib; sourceTree = "<group>"; };
DF32ABEA29494CF0008E3A12 /* Settings.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Settings.strings; sourceTree = "<group>"; };
DF3630EA2936183D00326FB8 /* OPMLDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLDocument.swift; sourceTree = "<group>"; };
DF3630EE293618A900326FB8 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
@@ -2041,11 +2031,6 @@
DF3630E92936038400326FB8 /* New Article Notifications */,
DF766FEA2936337A006FBBE2 /* Help */,
5137C2E926F63AE6009EFEDB /* ArticleThemeImporter.swift */,
510FFAB226EEA22C00F32265 /* ArticleThemesTableViewController.swift */,
51A16990235E10D600EB091F /* Settings.storyboard */,
DF32ABE629493192008E3A12 /* SettingsComboTableViewCell.swift */,
DF32ABE729493193008E3A12 /* SettingsComboTableViewCell.xib */,
516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */,
);
path = Settings;
sourceTree = "<group>";
@@ -3604,14 +3589,12 @@
DFB3499A294C4F1D00BC81AD /* Errors.strings in Resources */,
51D0214826ED617100FF2E0F /* core.css in Resources */,
84C9FCA42262A1B800D921D6 /* LaunchScreenPhone.storyboard in Resources */,
516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */,
511D43D1231FA62800FB1562 /* SidebarKeyboardShortcuts.plist in Resources */,
51C452AB22650DC600C03939 /* template.html in Resources */,
84A3EE61223B667F00557320 /* DefaultFeeds.opml in Resources */,
B27EEBFB244D15F3000932E6 /* stylesheet.css in Resources */,
DFB34984294B3AFF00BC81AD /* Buttons.strings in Resources */,
511D43CF231FA62200FB1562 /* DetailKeyboardShortcuts.plist in Resources */,
51A1699A235E10D700EB091F /* Settings.storyboard in Resources */,
49F40DF92335B71000552BF4 /* newsfoot.js in Resources */,
512392C024E33A3C00F11704 /* RedditAdd.storyboard in Resources */,
5177C21327B07CFE00643901 /* NewsFax.nnwtheme in Resources */,
@@ -3620,7 +3603,6 @@
DFCE4F9528EF278300405869 /* Thanks.md in Resources */,
51DEE81426FB9233006DAA56 /* Appanoose.nnwtheme in Resources */,
51E36E8C239D6765006F47A5 /* AddFeedSelectFolderTableViewCell.xib in Resources */,
DF32ABE929493193008E3A12 /* SettingsComboTableViewCell.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -4200,7 +4182,6 @@
51EAED96231363EF00A9EEE3 /* NonIntrinsicButton.swift in Sources */,
51C4527B2265091600C03939 /* MasterUnreadIndicatorView.swift in Sources */,
5186A635235EF3A800C97195 /* VibrantLabel.swift in Sources */,
DF32ABE829493193008E3A12 /* SettingsComboTableViewCell.swift in Sources */,
51F85BF92274AA7B00C787DC /* UIBarButtonItem-Extensions.swift in Sources */,
51B62E68233186730085F949 /* IconView.swift in Sources */,
179D280D26F73D83003B2E0A /* ArticleThemePlist.swift in Sources */,
@@ -4371,7 +4352,6 @@
51627A6723861DA3007B3B4B /* MasterFeedViewController+Drag.swift in Sources */,
51FFF0C4235EE8E5002762AA /* VibrantButton.swift in Sources */,
51C45259226508D300C03939 /* AppDefaults.swift in Sources */,
510FFAB326EEA22C00F32265 /* ArticleThemesTableViewController.swift in Sources */,
511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */,
51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */,
51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */,

View File

@@ -93,6 +93,7 @@ final class ArticleThemesManager: NSObject, NSFilePresenter, Logging, Observable
func themeExists(filename: String) -> Bool {
let filenameLastPathComponent = (filename as NSString).lastPathComponent
let toFilename = (folderPath as NSString).appendingPathComponent(filenameLastPathComponent)
logger.debug("\(filenameLastPathComponent), \(toFilename)")
return FileManager.default.fileExists(atPath: toFilename)
}
@@ -130,8 +131,12 @@ final class ArticleThemesManager: NSObject, NSFilePresenter, Logging, Observable
func deleteTheme(themeName: String) {
if let filename = pathForThemeName(themeName, folder: folderPath) {
try? FileManager.default.removeItem(atPath: filename)
objectWillChange.send()
do {
try FileManager.default.removeItem(atPath: filename)
} catch {
logger.error("\(error.localizedDescription)")
}
}
}

View File

@@ -19,3 +19,4 @@
"USE_CLOUDKIT_BUTTON_TITLE" = "Use iCloud";
"DELETE_THEME_BUTTON_TITLE" = "Delete Theme";
"IMPORT_THEME_BUTTON_TITLE" = "Import Theme";
"IMPORT_AND_OVERWRITE_THEME_BUTTON_TITLE" = "Overwrite Existing Theme";

View File

@@ -12,3 +12,4 @@
"USERNAME_AND_PASSWORD_REQUIRED" = "Username and password required.";
"INVALID_USERNAME_PASSWORD" = "Invalid email or password combination.";
"KEYCHAIN_ERROR" = "Keychain error while storing credentials.";
"DUPLICATE_DEFAULT_THEME" = "You cannot import a theme that shares the same name as a provided theme.";

View File

@@ -24,6 +24,8 @@ public enum LocalizedNetNewsWireError: LocalizedError {
case invalidUsernameOrPassword
case keychainError
case duplicateDefaultTheme
public var errorDescription: String? {
switch self {
@@ -37,6 +39,8 @@ public enum LocalizedNetNewsWireError: LocalizedError {
return Bundle.main.localizedString(forKey: "INVALID_USERNAME_PASSWORD", value: nil, table: "Errors")
case .keychainError:
return Bundle.main.localizedString(forKey: "KEYCHAIN_ERROR", value: nil, table: "Errors")
case .duplicateDefaultTheme:
return Bundle.main.localizedString(forKey: "DUPLICATE_DEFAULT_THEME", value: nil, table: "Errors")
}
}
}

View File

@@ -46,10 +46,12 @@
"INSTALLED_THEMES" = "Installed Themes";
"ARTICLE_THEMES_TITLE" = "Article Themes";
"ARTICLE_THEME" = "Article Theme";
"ARTICLE_THEME_CREATOR_%@" = "by %@";
"IMPORT_THEME_CONFIRMATION_TITLE" = "Import Theme";
"IMPORT_THEME_CONFIRMATION_MESSAGE_%@_%@" = "Are you sure you want to import “%@” by %@?";
"IMPORT_AND_OVERWRITE_THEME_CONFIRMATION_MESSAGE_%@" = "The theme “%@” already exists. Do you want to overwrite it?";
"IMPORT_THEME_SUCCESS_TITLE" = "Import Successful";
"IMPORT_THEME_SUCCESS_MESSAGE_%@" = "“%@” has been imported successfully.";
"IMPORT_THEME_SUCCESS_MESSAGE_%@" = "The theme “%@” has been imported.";
"DELETE_THEME_ALERT_TITLE_%@" = "Are you sure you want to delete “%@”?";
"DELETE_THEME_ALERT_MESSAGE" = "Are you sure you want to delete this theme? This action is not reversible.";
"CONFIRM_MARK_ALL_AS_READ" = "Confirm Mark All as Read";

View File

@@ -7,9 +7,9 @@
<key>ThemeIdentifier</key>
<string>com.mynameisstuart.themes.newsfax</string>
<key>CreatorHomePage</key>
<string>https://mynameisstuart.com/</string>
<string>https://stuartbreckenridge.net/</string>
<key>CreatorName</key>
<string>Stuart Breckenridge</string>
<string>Ranchero Software</string>
<key>Version</key>
<integer>3</integer>
</dict>

View File

@@ -7,9 +7,9 @@
<key>ThemeIdentifier</key>
<string>com.mynameisstuart.themes.promenade</string>
<key>CreatorHomePage</key>
<string>https://mynameisstuart.com/</string>
<string>https://stuartbreckenridge.net/</string>
<key>CreatorName</key>
<string>Stuart Breckenridge</string>
<string>Ranchero Software</string>
<key>Version</key>
<integer>14</integer>
</dict>

View File

@@ -44,7 +44,7 @@ struct ArticleThemeManagerView: View {
switch result {
case .success(let success):
do {
let theme = try ArticleTheme(path: success.path, isAppTheme: true)
let theme = try ArticleTheme(path: success.path, isAppTheme: false)
showImportConfirmationAlert = (theme, true)
} catch {
showImportErrorAlert = (error, true)
@@ -55,7 +55,7 @@ struct ArticleThemeManagerView: View {
}
.alert(Text("DELETE_THEME_ALERT_TITLE_\(showDeleteConfirmation.0)", tableName: "Settings"), isPresented: $showDeleteConfirmation.1, actions: {
Button(role: .destructive) {
ArticleThemesManager.shared.deleteTheme(themeName: showDeleteConfirmation.0)
themeManager.deleteTheme(themeName: showDeleteConfirmation.0)
} label: {
Text("DELETE_THEME_BUTTON_TITLE", tableName: "Buttons")
}
@@ -73,14 +73,27 @@ struct ArticleThemeManagerView: View {
actions: {
Button {
do {
try ArticleThemesManager.shared.importTheme(filename: showImportConfirmationAlert.0!.path!)
showImportSuccessAlert = true
if themeManager.themeExists(filename: showImportConfirmationAlert.0!.path!) {
if try! themeManager.articleThemeWithThemeName(showImportConfirmationAlert.0!.name).isAppTheme {
showImportErrorAlert = (LocalizedNetNewsWireError.duplicateDefaultTheme, true)
} else {
try themeManager.importTheme(filename: showImportConfirmationAlert.0!.path!)
showImportSuccessAlert = true
}
} else {
try themeManager.importTheme(filename: showImportConfirmationAlert.0!.path!)
showImportSuccessAlert = true
}
} catch {
showImportErrorAlert = (error, true)
}
} label: {
Text("IMPORT_THEME_BUTTON_TITLE", tableName: "Buttons")
let exists = themeManager.themeExists(filename: showImportConfirmationAlert.0?.path ?? "")
if exists == true {
Text("IMPORT_AND_OVERWRITE_THEME_BUTTON_TITLE", tableName: "Buttons")
} else {
Text("IMPORT_THEME_BUTTON_TITLE", tableName: "Buttons")
}
}
Button(role: .cancel) {
@@ -89,7 +102,12 @@ struct ArticleThemeManagerView: View {
Text("CANCEL_BUTTON_TITLE", tableName: "Buttons")
}
}, message: {
Text("IMPORT_THEME_CONFIRMATION_MESSAGE_\(showImportConfirmationAlert.0?.name ?? "")_\(showImportConfirmationAlert.0?.creatorName ?? "")", tableName: "Settings")
let exists = themeManager.themeExists(filename: showImportConfirmationAlert.0?.path ?? "")
if exists {
Text("IMPORT_AND_OVERWRITE_THEME_CONFIRMATION_MESSAGE_\(showImportConfirmationAlert.0?.name ?? "")", tableName: "Settings")
} else {
Text("IMPORT_THEME_CONFIRMATION_MESSAGE_\(showImportConfirmationAlert.0?.name ?? "")_\(showImportConfirmationAlert.0?.creatorName ?? "")", tableName: "Settings")
}
})
.alert(Text("IMPORT_THEME_SUCCESS_TITLE", tableName: "Settings"),
isPresented: $showImportSuccessAlert,
@@ -102,32 +120,52 @@ struct ArticleThemeManagerView: View {
}, message: {
Text("IMPORT_THEME_SUCCESS_MESSAGE_\(showImportConfirmationAlert.0?.name ?? "")", tableName: "Settings")
})
.alert(Text("ERROR_TITLE", tableName: "Errors"),
isPresented: $showImportErrorAlert.1,
actions: {
Button(role: .cancel) {
} label: {
Text("DISMISS_BUTTON_TITLE", tableName: "Buttons")
}
}, message: {
Text("\(showImportErrorAlert.0?.localizedDescription ?? "")")
})
}
func articleThemeRow(_ theme: String) -> some View {
Button {
ArticleThemesManager.shared.currentThemeName = theme
themeManager.currentThemeName = theme
} label: {
HStack {
Text(theme)
.foregroundColor(.primary)
VStack(alignment: .leading) {
Text(theme)
.foregroundColor(.primary)
if let articleTheme = try? themeManager.articleThemeWithThemeName(theme) {
Text("ARTICLE_THEME_CREATOR_\(articleTheme.creatorName)", tableName: "Settings")
.font(.caption)
.foregroundColor(.secondary)
}
}
Spacer()
if ArticleThemesManager.shared.currentThemeName == theme {
if themeManager.currentThemeName == theme {
Image(systemName: "checkmark")
.foregroundColor(Color(uiColor: AppAssets.primaryAccentColor))
}
}
}
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
if theme == "Default" || ArticleThemesManager.shared.currentThemeName == theme { }
else {
Button {
showDeleteConfirmation = (theme, true)
} label: {
Text("DELETE_BUTTON_TITLE", tableName: "Buttons")
Image(systemName: "trash")
if theme == themeManager.currentThemeName { }
if let currentTheme = try? themeManager.articleThemeWithThemeName(theme) {
if currentTheme.isAppTheme { } else {
Button {
showDeleteConfirmation = (theme, true)
} label: {
Text("DELETE_BUTTON_TITLE", tableName: "Buttons")
Image(systemName: "trash")
}
.tint(.red)
}
.tint(.red)
}
}
}

View File

@@ -1,150 +0,0 @@
//
// ArticleThemesTableViewController.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 9/12/21.
// Copyright © 2021 Ranchero Software. All rights reserved.
//
import Foundation
import UniformTypeIdentifiers
import RSCore
import UIKit
import SwiftUI
struct ArticleThemesWrapper: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> ArticleThemesTableViewController {
let storyboard = UIStoryboard(name: "Settings", bundle: .main)
let controller = storyboard.instantiateViewController(withIdentifier: "ArticleThemesTableViewController") as! ArticleThemesTableViewController
context.coordinator.parentObserver = controller.observe(\.parent, changeHandler: { vc, _ in
vc.parent?.title = vc.title
vc.parent?.navigationItem.rightBarButtonItems = vc.navigationItem.rightBarButtonItems
})
return controller
}
func updateUIViewController(_ uiViewController: ArticleThemesTableViewController, context: Context) {
//
}
typealias UIViewControllerType = ArticleThemesTableViewController
class Coordinator {
var parentObserver: NSKeyValueObservation?
}
func makeCoordinator() -> Self.Coordinator { Coordinator() }
}
class ArticleThemesTableViewController: UITableViewController, Logging {
override func viewDidLoad() {
let importBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(importTheme(_:)));
importBarButtonItem.title = NSLocalizedString("Import Theme", comment: "Import Theme");
navigationItem.rightBarButtonItem = importBarButtonItem
NotificationCenter.default.addObserver(self, selector: #selector(articleThemeNamesDidChangeNotification(_:)), name: .ArticleThemeNamesDidChangeNotification, object: nil)
}
// MARK: Notifications
@objc func articleThemeNamesDidChangeNotification(_ note: Notification) {
tableView.reloadData()
}
@objc func importTheme(_ sender: Any?) {
let docPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.nnwTheme], asCopy: true)
docPicker.delegate = self
docPicker.modalPresentationStyle = .formSheet
self.present(docPicker, animated: true)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ArticleThemesManager.shared.themeNames.count + 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let themeName: String
if indexPath.row == 0 {
themeName = ArticleTheme.defaultTheme.name
} else {
themeName = ArticleThemesManager.shared.themeNames[indexPath.row - 1]
}
cell.textLabel?.text = themeName
if themeName == ArticleThemesManager.shared.currentTheme.name {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath), let themeName = cell.textLabel?.text else { return }
ArticleThemesManager.shared.currentThemeName = themeName
navigationController?.popViewController(animated: true)
}
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
guard let cell = tableView.cellForRow(at: indexPath),
let themeName = cell.textLabel?.text else { return nil }
guard let theme = try? 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
let title = NSLocalizedString("Delete Theme?", comment: "Delete Theme")
let localizedMessageText = NSLocalizedString("Are you sure you want to delete the theme “%@”?.", comment: "Delete Theme Message")
let message = NSString.localizedStringWithFormat(localizedMessageText as NSString, themeName) as String
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel) { action in
completion(true)
}
alertController.addAction(cancelAction)
let deleteTitle = NSLocalizedString("Delete", comment: "Delete")
let deleteAction = UIAlertAction(title: deleteTitle, style: .destructive) { action in
ArticleThemesManager.shared.deleteTheme(themeName: themeName)
completion(true)
}
alertController.addAction(deleteAction)
self?.present(alertController, animated: true)
}
deleteAction.image = AppAssets.trashImage
deleteAction.backgroundColor = UIColor.systemRed
return UISwipeActionsConfiguration(actions: [deleteAction])
}
}
// MARK: UIDocumentPickerDelegate
extension ArticleThemesTableViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let url = urls.first else { return }
try ArticleThemeImporter.importTheme(controller: self, filename: url.standardizedFileURL.path)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,30 +0,0 @@
//
// SettingsAccountTableViewCell.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 10/23/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import UIKit
class SettingsComboTableViewCell: VibrantTableViewCell {
@IBOutlet weak var comboImage: UIImageView!
@IBOutlet weak var comboNameLabel: UILabel!
override func updateVibrancy(animated: Bool) {
super.updateVibrancy(animated: animated)
updateLabelVibrancy(comboNameLabel, color: labelColor, animated: animated)
let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : UIColor.label
if animated {
UIView.animate(withDuration: Self.duration) {
self.comboImage?.tintColor = tintColor
}
} else {
self.comboImage?.tintColor = tintColor
}
}
}

View File

@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="JCb-QB-CrO" customClass="SettingsComboTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="JCb-QB-CrO" id="FzD-t2-JGy">
<rect key="frame" x="0.0" y="0.0" width="383" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="yiw-9t-gil">
<rect key="frame" x="12" y="11" width="22" height="22"/>
<constraints>
<constraint firstAttribute="width" constant="22" id="43E-Em-Z6O"/>
<constraint firstAttribute="height" constant="22" id="mTY-cQ-1R1"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TRx-RV-za8">
<rect key="frame" x="42" y="14" width="42" height="16"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="TRx-RV-za8" firstAttribute="leading" secondItem="yiw-9t-gil" secondAttribute="trailing" constant="8" symbolic="YES" id="RUN-Ol-xSl"/>
<constraint firstItem="TRx-RV-za8" firstAttribute="top" secondItem="FzD-t2-JGy" secondAttribute="top" constant="14" id="cze-hi-8Uh"/>
<constraint firstItem="yiw-9t-gil" firstAttribute="leading" secondItem="FzD-t2-JGy" secondAttribute="leading" constant="12" id="oU9-E3-lEt"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="TRx-RV-za8" secondAttribute="trailing" constant="8" id="sJ6-wr-JIw"/>
<constraint firstItem="yiw-9t-gil" firstAttribute="centerY" secondItem="FzD-t2-JGy" secondAttribute="centerY" id="tUD-tI-dgr"/>
<constraint firstAttribute="bottom" secondItem="TRx-RV-za8" secondAttribute="bottom" constant="14" id="zls-MW-Ffp"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="comboImage" destination="yiw-9t-gil" id="WqT-gf-Pwq"/>
<outlet property="comboNameLabel" destination="TRx-RV-za8" id="CX9-Cp-qZP"/>
</connections>
<point key="canvasLocation" x="7" y="-9"/>
</tableViewCell>
</objects>
</document>

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="SettingsTableViewCell" id="JCb-QB-CrO" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="JCb-QB-CrO" id="FzD-t2-JGy">
<rect key="frame" x="0.0" y="0.0" width="385.5" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
<point key="canvasLocation" x="7" y="-9"/>
</tableViewCell>
</objects>
</document>