Revised settings views

Notes:
- A lot of representable shims over UIKit view controllers. These misbehave a little. They should be re-written in SwiftUI.
- Settings no longer uses VibrantTableViewCell
- Changes to AppDefaults trigger objectWillSend
This commit is contained in:
Stuart Breckenridge
2022-11-12 19:50:13 +08:00
parent 35440ba542
commit c8306c8660
12 changed files with 636 additions and 52 deletions

View File

@@ -860,6 +860,10 @@
DFCE4F9228EF26F100405869 /* About.plist in Resources */ = {isa = PBXBuildFile; fileRef = DFCE4F9028EF26F000405869 /* About.plist */; };
DFCE4F9428EF278300405869 /* Thanks.md in Resources */ = {isa = PBXBuildFile; fileRef = DFCE4F9328EF278300405869 /* Thanks.md */; };
DFCE4F9528EF278300405869 /* Thanks.md in Resources */ = {isa = PBXBuildFile; fileRef = DFCE4F9328EF278300405869 /* Thanks.md */; };
DFD406F5291F79C900C02962 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD406F4291F79C900C02962 /* SettingsView.swift */; };
DFD406F7291FB1A600C02962 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD406F6291FB1A600C02962 /* SafariView.swift */; };
DFD406FA291FB5E400C02962 /* SettingsRows.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD406F9291FB5E400C02962 /* SettingsRows.swift */; };
DFD406FC291FB63B00C02962 /* SettingsHelpSheets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD406FB291FB63B00C02962 /* SettingsHelpSheets.swift */; };
DFFB8FC2279B75E300AC21D7 /* Account in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 51BC2F4A24D343A500E90810 /* Account */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
DFFC199827A0D0D7004B7AEF /* NotificationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFFC199727A0D0D7004B7AEF /* NotificationsViewController.swift */; };
DFFC199A27A0D32A004B7AEF /* NotificationsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFFC199927A0D32A004B7AEF /* NotificationsTableViewCell.swift */; };
@@ -1601,6 +1605,10 @@
DFC14F1628EB17A800F6EE86 /* CreditsNetNewsWireView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsNetNewsWireView.swift; sourceTree = "<group>"; };
DFCE4F9028EF26F000405869 /* About.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = About.plist; sourceTree = "<group>"; };
DFCE4F9328EF278300405869 /* Thanks.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Thanks.md; sourceTree = "<group>"; };
DFD406F4291F79C900C02962 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
DFD406F6291FB1A600C02962 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
DFD406F9291FB5E400C02962 /* SettingsRows.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRows.swift; sourceTree = "<group>"; };
DFD406FB291FB63B00C02962 /* SettingsHelpSheets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsHelpSheets.swift; sourceTree = "<group>"; };
DFD6AACB27ADE80900463FAD /* NewsFax.nnwtheme */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = NewsFax.nnwtheme; sourceTree = "<group>"; };
DFFC199727A0D0D7004B7AEF /* NotificationsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsViewController.swift; sourceTree = "<group>"; };
DFFC199927A0D32A004B7AEF /* NotificationsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsTableViewCell.swift; sourceTree = "<group>"; };
@@ -2000,6 +2008,7 @@
516244E2241E19F000B61C47 /* ColorPaletteTableViewController.swift */,
519ED47924482AEB007F8E94 /* EnableExtensionPointViewController.swift */,
51A16990235E10D600EB091F /* Settings.storyboard */,
DFD406F8291FB5D500C02962 /* Settings View */,
516A09382360A2AE00EAE89B /* SettingsComboTableViewCell.swift */,
516A091D23609A3600EAE89B /* SettingsComboTableViewCell.xib */,
516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */,
@@ -2059,6 +2068,7 @@
5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */,
5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */,
51A9A6092382FD240033AADF /* PoppableGestureRecognizerDelegate.swift */,
DFD406F6291FB1A600C02962 /* SafariView.swift */,
51C45250226506F400C03939 /* String-Extensions.swift */,
5108F6D723763094001ABC45 /* TickMarkSlider.swift */,
C5A6ED6C23C9B0C800AB6BE2 /* UIActivityViewController-Extensions.swift */,
@@ -2882,6 +2892,16 @@
path = About;
sourceTree = "<group>";
};
DFD406F8291FB5D500C02962 /* Settings View */ = {
isa = PBXGroup;
children = (
DFD406F4291F79C900C02962 /* SettingsView.swift */,
DFD406F9291FB5E400C02962 /* SettingsRows.swift */,
DFD406FB291FB63B00C02962 /* SettingsHelpSheets.swift */,
);
path = "Settings View";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -4062,6 +4082,7 @@
51C452772265091600C03939 /* MultilineUILabelSizer.swift in Sources */,
51C452A522650A2D00C03939 /* SmallIconProvider.swift in Sources */,
51AB8AB323B7F4C6008F147D /* WebViewController.swift in Sources */,
DFD406F7291FB1A600C02962 /* SafariView.swift in Sources */,
516A09392360A2AE00EAE89B /* SettingsComboTableViewCell.swift in Sources */,
176813D22564BA5900D98635 /* WidgetDataEncoder.swift in Sources */,
51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */,
@@ -4081,6 +4102,7 @@
51C4526B226508F600C03939 /* MasterFeedViewController.swift in Sources */,
5126EE97226CB48A00C22AFC /* SceneCoordinator.swift in Sources */,
84CAFCB022BC8C35007694F0 /* FetchRequestOperation.swift in Sources */,
DFD406FA291FB5E400C02962 /* SettingsRows.swift in Sources */,
5193CD5A245E44A90092735E /* RedditFeedProvider-Extensions.swift in Sources */,
51EF0F77227716200050506E /* FaviconGenerator.swift in Sources */,
51938DF3231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */,
@@ -4151,6 +4173,7 @@
51C452A922650DC600C03939 /* ArticleRenderer.swift in Sources */,
51C45297226509E300C03939 /* DefaultFeedsImporter.swift in Sources */,
512E094D2268B8AB00BDCFDD /* DeleteCommand.swift in Sources */,
DFD406FC291FB63B00C02962 /* SettingsHelpSheets.swift in Sources */,
5110C37D2373A8D100A9C04F /* InspectorIconHeaderView.swift in Sources */,
51F85BFB2275D85000C787DC /* Array-Extensions.swift in Sources */,
515A5180243E90260089E588 /* TwitterFeedProvider-Extensions.swift in Sources */,
@@ -4176,6 +4199,7 @@
51DC370B2405BC9A0095D371 /* PreloadedWebView.swift in Sources */,
D3555BF524664566005E48C3 /* ArticleSearchBar.swift in Sources */,
8454C3F3263F2D8700E3F9C7 /* IconImageCache.swift in Sources */,
DFD406F5291F79C900C02962 /* SettingsView.swift in Sources */,
B24E9ADE245AB88400DA5718 /* NSAttributedString+NetNewsWire.swift in Sources */,
C5A6ED5223C9AF4300AB6BE2 /* TitleActivityItemSource.swift in Sources */,
17071EF126F8137400F5E71D /* ArticleTheme+Notifications.swift in Sources */,

View File

@@ -8,13 +8,14 @@
import Foundation
import RSCore
import Combine
public extension Notification.Name {
static let ArticleThemeNamesDidChangeNotification = Notification.Name("ArticleThemeNamesDidChangeNotification")
static let CurrentArticleThemeDidChangeNotification = Notification.Name("CurrentArticleThemeDidChangeNotification")
}
final class ArticleThemesManager: NSObject, NSFilePresenter, Logging {
final class ArticleThemesManager: NSObject, NSFilePresenter, Logging, ObservableObject {
static var shared: ArticleThemesManager!
public let folderPath: String

View File

@@ -7,6 +7,8 @@
//
import UIKit
import Combine
import SwiftUI
enum UserInterfaceColorPalette: Int, CustomStringConvertible, CaseIterable {
case automatic = 0
@@ -26,7 +28,7 @@ enum UserInterfaceColorPalette: Int, CustomStringConvertible, CaseIterable {
}
final class AppDefaults {
final class AppDefaults: ObservableObject {
static let defaultThemeName = "Default"
@@ -85,6 +87,7 @@ final class AppDefaults {
}
set {
setInt(for: Key.userInterfaceColorPalette, newValue.rawValue)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -94,6 +97,7 @@ final class AppDefaults {
}
set {
AppDefaults.setString(for: Key.addWebFeedAccountID, newValue)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -103,6 +107,7 @@ final class AppDefaults {
}
set {
AppDefaults.setString(for: Key.addWebFeedFolderName, newValue)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -112,6 +117,7 @@ final class AppDefaults {
}
set {
AppDefaults.setString(for: Key.addFolderAccountID, newValue)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -121,6 +127,7 @@ final class AppDefaults {
}
set {
UserDefaults.standard.set(newValue, forKey: Key.activeExtensionPointIDs)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -130,6 +137,7 @@ final class AppDefaults {
}
set {
UserDefaults.standard.set(newValue, forKey: Key.hasUsedFullScreenPreviously)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -139,6 +147,7 @@ final class AppDefaults {
}
set {
UserDefaults.standard.setValue(newValue, forKey: Key.useSystemBrowser)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -148,6 +157,7 @@ final class AppDefaults {
}
set {
AppDefaults.setDate(for: Key.lastImageCacheFlushDate, newValue)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -157,6 +167,7 @@ final class AppDefaults {
}
set {
AppDefaults.setBool(for: Key.timelineGroupByFeed, newValue)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -166,6 +177,7 @@ final class AppDefaults {
}
set {
AppDefaults.setBool(for: Key.refreshClearsReadArticles, newValue)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -175,15 +187,33 @@ final class AppDefaults {
}
set {
AppDefaults.setSortDirection(for: Key.timelineSortDirection, newValue)
AppDefaults.shared.objectWillChange.send()
}
}
/// This is a `Bool` wrapper for `timelineSortDirection`'s
/// `ComparisonResult`
var timelineSortDirectionBool: Bool {
get {
if AppDefaults.shared.timelineSortDirection == .orderedAscending {
return true
}
return false
}
set {
if newValue == true { timelineSortDirection = .orderedAscending } else {
timelineSortDirection = .orderedDescending
}
}
}
var articleFullscreenEnabled: Bool {
get {
return AppDefaults.bool(for: Key.articleFullscreenEnabled)
}
set {
AppDefaults.setBool(for: Key.articleFullscreenEnabled, newValue)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -193,6 +223,7 @@ final class AppDefaults {
}
set {
AppDefaults.setBool(for: Key.confirmMarkAllAsRead, newValue)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -202,6 +233,7 @@ final class AppDefaults {
}
set {
AppDefaults.setDate(for: Key.lastRefresh, newValue)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -211,6 +243,7 @@ final class AppDefaults {
}
set {
AppDefaults.setInt(for: Key.timelineNumberOfLines, newValue)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -221,6 +254,7 @@ final class AppDefaults {
}
set {
AppDefaults.store.set(newValue.rawValue, forKey: Key.timelineIconDimension)
AppDefaults.shared.objectWillChange.send()
}
}
@@ -230,6 +264,7 @@ final class AppDefaults {
}
set {
AppDefaults.setString(for: Key.currentThemeName, newValue)
AppDefaults.shared.objectWillChange.send()
}
}

View File

@@ -13,6 +13,7 @@ import Articles
import RSCore
import RSTree
import SafariServices
import SwiftUI
protocol MainControllerIdentifiable {
var mainControllerIdentifer: MainControllerIdentifier { get }
@@ -1129,12 +1130,14 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, Logging {
}
func showSettings(scrollToArticlesSection: Bool = false) {
let settingsNavController = UIStoryboard.settings.instantiateInitialViewController() as! UINavigationController
let settingsViewController = settingsNavController.topViewController as! SettingsViewController
settingsViewController.scrollToArticlesSection = scrollToArticlesSection
settingsNavController.modalPresentationStyle = .formSheet
settingsViewController.presentingParentController = rootSplitViewController
rootSplitViewController.present(settingsNavController, animated: true)
// let settingsNavController = UIStoryboard.settings.instantiateInitialViewController() as! UINavigationController
// let settingsViewController = settingsNavController.topViewController as! SettingsViewController
// settingsViewController.scrollToArticlesSection = scrollToArticlesSection
// settingsNavController.modalPresentationStyle = .formSheet
// settingsViewController.presentingParentController = rootSplitViewController
// rootSplitViewController.present(settingsNavController, animated: true)
let hostedSettings = UIHostingController(rootView: SettingsView())
rootSplitViewController.present(hostedSettings, animated: true)
}
func showAccountInspector(for account: Account) {

View File

@@ -8,8 +8,37 @@
import Account
import UIKit
import SwiftUI
import RSCore
struct AddAccountViewControllerRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> AddAccountViewController {
let storyboard = UIStoryboard(name: "Settings", bundle: .main)
let controller = storyboard.instantiateViewController(withIdentifier: "AddAccountViewController") as! AddAccountViewController
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: AddAccountViewController, context: Context) {
//
}
typealias UIViewControllerType = AddAccountViewController
class Coordinator {
var parentObserver: NSKeyValueObservation?
}
func makeCoordinator() -> Self.Coordinator { Coordinator() }
}
protocol AddAccountDismissDelegate: UIViewController {
func dismiss()
}

View File

@@ -10,6 +10,36 @@ import Foundation
import UniformTypeIdentifiers
import RSCore
import UIKit
import SwiftUI
struct ArticleThemesViewControllerRepresentable: 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 {

View File

@@ -7,9 +7,38 @@
//
import UIKit
import SwiftUI
import Account
import UserNotifications
struct NotificationsViewControllerRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> NotificationsViewController {
let storyboard = UIStoryboard(name: "Settings", bundle: .main)
let controller = storyboard.instantiateViewController(withIdentifier: "NotificationsViewController") as! NotificationsViewController
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: NotificationsViewController, context: Context) {
//
}
typealias UIViewControllerType = NotificationsViewController
class Coordinator {
var parentObserver: NSKeyValueObservation?
}
func makeCoordinator() -> Self.Coordinator { Coordinator() }
}
class NotificationsViewController: UIViewController {
@IBOutlet weak var notificationsTableView: UITableView!
@@ -89,9 +118,9 @@ class NotificationsViewController: UIViewController {
private func reloadVisibleCells(_ notification: Notification) {
guard let faviconURLString = notification.userInfo?["faviconURL"] as? String,
let faviconHost = URL(string: faviconURLString)?.host else {
return
}
return
}
for cell in notificationsTableView.visibleCells {
if let notificationCell = cell as? NotificationsTableViewCell {
if let feedURLHost = URL(string: notificationCell.feed!.url)?.host {
@@ -117,21 +146,21 @@ class NotificationsViewController: UIViewController {
let filterMenu = UIMenu(title: "",
image: nil,
identifier: nil,
options: [.displayInline],
children: [
UIAction(
title: NSLocalizedString("Show Feeds with Notifications Enabled", comment: "Feeds with Notifications"),
image: nil,
identifier: nil,
discoverabilityTitle: nil,
attributes: [],
state: newArticleNotificationFilter ? .on : .off,
handler: { [weak self] _ in
self?.newArticleNotificationFilter.toggle()
self?.notificationsTableView.reloadData()
})])
options: [.displayInline],
children: [
UIAction(
title: NSLocalizedString("Show Feeds with Notifications Enabled", comment: "Feeds with Notifications"),
image: nil,
identifier: nil,
discoverabilityTitle: nil,
attributes: [],
state: newArticleNotificationFilter ? .on : .off,
handler: { [weak self] _ in
self?.newArticleNotificationFilter.toggle()
self?.notificationsTableView.reloadData()
})])
var menus = [UIMenuElement]()

View File

@@ -0,0 +1,77 @@
//
// SettingsHelpSheets.swift
// NetNewsWire-iOS
//
// Created by Stuart Breckenridge on 12/11/2022.
// Copyright © 2022 Ranchero Software. All rights reserved.
//
import Foundation
enum HelpSheet: CustomStringConvertible, CaseIterable {
case help, website, releaseNotes, howToSupport, gitHubRepository, bugTracker, technotes, slack
var description: String {
switch self {
case .help:
return NSLocalizedString("NetNewsWire Help", comment: "NetNewsWire Help")
case .website:
return NSLocalizedString("Website", comment: "Website")
case .releaseNotes:
return NSLocalizedString("Release Notes", comment: "Release Notes")
case .howToSupport:
return NSLocalizedString("How to Support NetNewsWire", comment: "How to Support")
case .gitHubRepository:
return NSLocalizedString("GitHub Respository", comment: "Github")
case .bugTracker:
return NSLocalizedString("Bug Tracker", comment: "Bug Tracker")
case .technotes:
return NSLocalizedString("Technotes", comment: "Technotes")
case .slack:
return NSLocalizedString("Slack", comment: "Slack")
}
}
var url: URL {
switch self {
case .help:
return URL(string: "https://netnewswire.com/help/ios/6.1/en/")!
case .website:
return URL(string: "https://netnewswire.com/")!
case .releaseNotes:
return URL(string: URL.releaseNotes.absoluteString)!
case .howToSupport:
return URL(string: "https://github.com/brentsimmons/NetNewsWire/blob/main/Technotes/HowToSupportNetNewsWire.markdown")!
case .gitHubRepository:
return URL(string: "https://github.com/brentsimmons/NetNewsWire")!
case .bugTracker:
return URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!
case .technotes:
return URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/main/Technotes")!
case .slack:
return URL(string: "https://netnewswire.com/slack")!
}
}
var systemImage: String {
switch self {
case .help:
return "questionmark.app"
case .website:
return "globe"
case .releaseNotes:
return "quote.opening"
case .howToSupport:
return "person.3.fill"
case .gitHubRepository:
return "archivebox"
case .bugTracker:
return "ladybug"
case .technotes:
return "chevron.left.slash.chevron.right"
case .slack:
return "quote.bubble.fill"
}
}
}

View File

@@ -0,0 +1,256 @@
//
// SettingsRows.swift
// NetNewsWire-iOS
//
// Created by Stuart Breckenridge on 12/11/2022.
// Copyright © 2022 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
// MARK: - Headers
struct SettingsViewHeaders {
static func AddAccountHeader(_ showAddAccount: Binding<Bool>) -> some View {
HStack {
Text("Accounts")
Spacer()
Button {
showAddAccount.wrappedValue.toggle()
} label: {
Text("Add")
.font(.caption)
.bold()
Image(systemName: "plus")
.font(.caption)
}
.buttonBorderShape(.capsule)
.buttonStyle(.bordered)
.padding(.trailing, -15) // moves to trailing edge
}
}
}
// MARK: - Rows
struct SettingsViewRows {
/// This row, when tapped, will open system settings.
static var OpenSystemSettings: some View {
Label {
Text("Open System Settings")
} icon: {
Image(systemName: "gear.circle.fill")
.resizable()
.renderingMode(.template)
.foregroundColor(.gray)
.aspectRatio(contentMode: .fit)
.frame(width: 25.0, height: 25.0)
}
.onTapGesture {
UIApplication.shared.open(URL(string: "\(UIApplication.openSettingsURLString)")!)
}
}
/// This row, when tapped, will push the New Article Notifications
/// screen in to view.
static var ConfigureNewArticleNotifications: some View {
NavigationLink(destination: NotificationsViewControllerRepresentable().edgesIgnoringSafeArea(.all)) {
Label {
Text("New Article Notifications")
} icon: {
Image(systemName: "bell.square.fill")
.resizable()
.renderingMode(.template)
.foregroundColor(.red)
.aspectRatio(contentMode: .fit)
.frame(width: 25.0, height: 25.0)
}
}
}
/// This row, when tapped, will push the the Add Account screen
/// in to view.
static var AddAccount: some View {
NavigationLink(destination: AddAccountViewControllerRepresentable().edgesIgnoringSafeArea(.all)) {
Label {
Text("Add Account")
} icon: {
Image(systemName: "plus.app.fill")
.resizable()
.renderingMode(.template)
.foregroundColor(Color(uiColor: AppAssets.primaryAccentColor))
.aspectRatio(contentMode: .fit)
.frame(width: 25.0, height: 25.0)
}
}
}
/// This `view` creates a `Label` for each active `Account`.
/// Each `Label`, when tapped, will present the configurator for
/// the `Account`.
static var ActiveAccounts: some View {
ForEach(AccountManager.shared.sortedActiveAccounts, id: \.self) { account in
Label {
Text(account.nameForDisplay)
} icon: {
Image(uiImage: AppAssets.image(for: account.type)!)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25.0, height: 25.0)
}
}
}
/// This row, when tapped, will push the the Add Extension screen
/// in to view.
static var AddExtension: some View {
NavigationLink(destination: NotificationsViewControllerRepresentable()) {
Label {
Text("Add Extension")
} icon: {
Image(systemName: "puzzlepiece.extension")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25.0, height: 25.0)
}
}
}
/// This row, when tapped, will push the the Import subscriptions screen
/// in to view.
static var ImportSubscription: some View {
Label {
Text("Import Subscriptions")
} icon: {
Image(systemName: "square.and.arrow.down")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25.0, height: 25.0)
}
}
/// This row, when tapped, will push the the Export subscriptions screen
/// in to view.
static var ExportSubscription: some View {
Label {
Text("Import Subscriptions")
} icon: {
Image(systemName: "square.and.arrow.up")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25.0, height: 25.0)
}
}
/// Returns a `Toggle` which triggers changes to the user's sort order preference.
/// - Parameter preference: `Binding<Bool>`
/// - Returns: `Toggle`
static func SortOldestToNewest(_ preference: Binding<Bool>) -> some View {
Toggle("Sort Oldest to Newest", isOn: preference)
}
/// Returns a `Toggle` which triggers changes to the user's grouping preference.
/// - Parameter preference: `Binding<Bool>`
/// - Returns: `Toggle`
static func GroupByFeed(_ preference: Binding<Bool>) -> some View {
Toggle("Group by Feed", isOn: preference)
}
/// Returns a `Toggle` which triggers changes to the user's refresh to clear preferences.
/// - Parameter preference: `Binding<Bool>`
/// - Returns: `Toggle`
static func RefreshToClearReadArticles(_ preference: Binding<Bool>) -> some View {
Toggle("Refresh To Clear Read Articles", isOn: preference)
}
/// This row, when tapped, will push the the Timeline Layout screen
/// in to view.
static var TimelineLayout: some View {
NavigationLink(destination: NotificationsViewControllerRepresentable()) {
Label {
Text("Timeline Layout")
} icon: {
Image(systemName: "slider.vertical.3")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25.0, height: 25.0)
}
}
}
static var ThemeSelection: some View {
NavigationLink(destination: ArticleThemesViewControllerRepresentable().edgesIgnoringSafeArea(.all)) {
Label {
Text("Article Themes")
} icon: {
Image(systemName: "textformat")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25.0, height: 25.0)
}
}
}
static func ConfirmMarkAllAsRead(_ preference: Binding<Bool>) -> some View {
Toggle("Confirm Mark All as Read", isOn: preference)
}
static func OpenLinksInNetNewsWire(_ preference: Binding<Bool>) -> some View {
Toggle("Open Links in NetNewsWire", isOn: preference)
}
static func EnableFullScreenArticles(_ preference: Binding<Bool>) -> some View {
Toggle(isOn: preference) {
VStack(alignment: .leading, spacing: 4) {
Text("Enable Full Screen Articles")
Text("Tap the article top bar to enter Full Screen. Tap the top or bottom to exit.")
.font(.caption)
.foregroundColor(.gray)
}
}
}
/// This row, when tapped, will push the New Article Notifications
/// screen in to view.
static var ConfigureAppearance: some View {
NavigationLink(destination: NotificationsViewControllerRepresentable().edgesIgnoringSafeArea(.all)) {
Label {
Text("Configure Appearance")
} icon: {
Image(systemName: "rectangle.trailinghalf.filled")
.resizable()
.renderingMode(.template)
.foregroundColor(.black)
.aspectRatio(contentMode: .fit)
.frame(width: 25.0, height: 25.0)
}
}
}
/// Sets the help sheet the user wishes to see.
/// - Parameters:
/// - sheet: The sheet provided to create the view.
/// - selectedSheet: A `Binding` to the currently selected sheet. This is set, followed by `show`.
/// - show: A `Binding` to `Bool` which triggers the sheet to display.
/// - Returns: `View`
static func ShowHelpSheet(sheet: HelpSheet, selectedSheet: Binding<HelpSheet>, _ show: Binding<Bool>) -> some View {
Label {
Text(sheet.description)
} icon: {
Image(systemName: sheet.systemImage)
.resizable()
.renderingMode(.template)
.foregroundColor(Color(uiColor: .tertiaryLabel))
.aspectRatio(contentMode: .fit)
.frame(width: 25.0, height: 25.0)
}
.onTapGesture {
selectedSheet.wrappedValue = sheet
show.wrappedValue.toggle()
}
}
}

View File

@@ -0,0 +1,76 @@
//
// SettingsView.swift
// NetNewsWire-iOS
//
// Created by Stuart Breckenridge on 12/11/2022.
// Copyright © 2022 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
struct SettingsView: View {
@StateObject private var appDefaults = AppDefaults.shared
@State private var showAddAccountView: Bool = false
@State private var helpSheet: HelpSheet = .help
@State private var showHelpSheet: Bool = false
var body: some View {
NavigationView {
List {
Section("Notifications, Badge, Data, and More") {
SettingsViewRows.OpenSystemSettings
SettingsViewRows.ConfigureNewArticleNotifications
}
Section(header: SettingsViewHeaders.AddAccountHeader($showAddAccountView)) {
SettingsViewRows.ActiveAccounts
}
Section("Extensions") {
SettingsViewRows.AddExtension
}
Section("Subscriptions") {
SettingsViewRows.ImportSubscription
SettingsViewRows.ExportSubscription
}
Section("Timeline") {
SettingsViewRows.SortOldestToNewest($appDefaults.timelineSortDirectionBool)
SettingsViewRows.GroupByFeed($appDefaults.timelineGroupByFeed)
SettingsViewRows.RefreshToClearReadArticles($appDefaults.refreshClearsReadArticles)
SettingsViewRows.TimelineLayout
}
Section("Articles") {
SettingsViewRows.ThemeSelection
SettingsViewRows.ConfirmMarkAllAsRead($appDefaults.confirmMarkAllAsRead)
SettingsViewRows.OpenLinksInNetNewsWire($appDefaults.useSystemBrowser)
SettingsViewRows.EnableFullScreenArticles($appDefaults.articleFullscreenEnabled)
}
Section("Appearance") {
SettingsViewRows.ConfigureAppearance
}
Section("Help") {
ForEach(0..<HelpSheet.allCases.count, id: \.self) { i in
SettingsViewRows.ShowHelpSheet(sheet: HelpSheet.allCases[i], selectedSheet: $helpSheet, $showHelpSheet)
}
}
}
.tint(Color(uiColor: AppAssets.primaryAccentColor))
.listStyle(.insetGrouped)
.navigationTitle(Text("Settings"))
.navigationBarTitleDisplayMode(.inline)
.sheet(isPresented: $showAddAccountView) {
AddAccountViewControllerRepresentable().edgesIgnoringSafeArea(.all)
}
.sheet(isPresented: $showHelpSheet) {
SafariView(url: helpSheet.url)
}
}
}
}

View File

@@ -282,20 +282,20 @@
<tableViewSection headerTitle="Articles" id="TRr-Ew-IvU">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="WFP-zj-Pve" customClass="VibrantBasicTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="814" width="374" height="43.5"/>
<rect key="frame" x="20" y="814" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="WFP-zj-Pve" id="DCX-wc-LSo">
<rect key="frame" x="0.0" y="0.0" width="343.5" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="343.5" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Theme" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bbw-L9-a3X">
<rect key="frame" x="20" y="11.5" width="53" height="20.5"/>
<rect key="frame" x="20" y="12" width="53" height="20.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Default" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DUf-DZ-3Nm">
<rect key="frame" x="280.5" y="11.5" width="55" height="20.5"/>
<rect key="frame" x="280.5" y="12" width="55" height="20.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@@ -314,19 +314,19 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="SXs-NQ-y3U" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="857.5" width="374" height="43.5"/>
<rect key="frame" x="20" y="858" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="SXs-NQ-y3U" id="BpI-Hz-KH2">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="Confirm Mark All as Read" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5tY-5k-v2g">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" ambiguous="YES" text="Confirm Mark All as Read" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5tY-5k-v2g">
<rect key="frame" x="20" y="11" width="191.5" height="21.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="UOo-9z-IuL">
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="UOo-9z-IuL">
<rect key="frame" x="307" y="6.5" width="51" height="31"/>
<color key="onTintColor" name="primaryAccentColor"/>
<connections>
@@ -347,19 +347,19 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="EYf-v1-lNi" customClass="VibrantBasicTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="901" width="374" height="48"/>
<rect key="frame" x="20" y="902" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EYf-v1-lNi" id="7nz-0Y-HaW">
<rect key="frame" x="0.0" y="0.0" width="374" height="48"/>
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="Open Links in NetNewsWire" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wm4-Y1-7nX">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" ambiguous="YES" text="Open Links in NetNewsWire" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wm4-Y1-7nX">
<rect key="frame" x="20" y="14" width="279" height="21"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dhR-L2-PX3" userLabel="Open Links in NetNewsWire">
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dhR-L2-PX3" userLabel="Open Links in NetNewsWire">
<rect key="frame" x="307" y="8.5" width="51" height="31"/>
<color key="onTintColor" name="primaryAccentColor"/>
<connections>
@@ -385,7 +385,7 @@
<tableViewSection headerTitle="Appearance" id="TkH-4v-yhk">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="EvG-yE-gDF" customClass="VibrantBasicTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="1005" width="374" height="43.5"/>
<rect key="frame" x="20" y="981.5" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EvG-yE-gDF" id="wBN-zJ-6pN">
<rect key="frame" x="0.0" y="0.0" width="343.5" height="43.5"/>
@@ -421,14 +421,14 @@
<tableViewSection headerTitle="Help" id="CS8-fJ-ghn">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="uGk-2d-oFc" style="IBUITableViewCellStyleDefault" id="Tle-IV-D40" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="1063" width="374" height="43.5"/>
<rect key="frame" x="20" y="1101.5" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Tle-IV-D40" id="IJD-ZB-8Wm">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" ambiguous="YES" insetsLayoutMarginsFromSafeArea="NO" text="NetNewsWire Help" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="uGk-2d-oFc">
<rect key="frame" x="20" y="0.0" width="334" height="43.5"/>
<rect key="frame" x="8" y="0.0" width="358" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
@@ -438,7 +438,7 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="6G3-yV-Eyh" style="IBUITableViewCellStyleDefault" id="Tbf-fE-nfx" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="1148" width="374" height="43.5"/>
<rect key="frame" x="20" y="1145" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Tbf-fE-nfx" id="beV-vI-g3r">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
@@ -455,7 +455,7 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="NeD-y8-KrM" style="IBUITableViewCellStyleDefault" id="TIX-yK-rC6" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="1191.5" width="374" height="43.5"/>
<rect key="frame" x="20" y="1188.5" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="TIX-yK-rC6" id="qr8-EN-Ofg">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
@@ -472,7 +472,7 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="lfL-bQ-sOp" style="IBUITableViewCellStyleDefault" id="mFn-fE-zqa" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="1235" width="374" height="43.5"/>
<rect key="frame" x="20" y="1232" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="mFn-fE-zqa" id="jTe-mf-MRj">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
@@ -489,7 +489,7 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="DDJ-8P-3YY" style="IBUITableViewCellStyleDefault" id="iGs-ze-4gQ" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="1278.5" width="374" height="43.5"/>
<rect key="frame" x="20" y="1275.5" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="iGs-ze-4gQ" id="EqZ-rF-N0l">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
@@ -506,7 +506,7 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="DsV-Qv-X4K" style="IBUITableViewCellStyleDefault" id="taJ-sg-wnU" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="1322" width="374" height="43.5"/>
<rect key="frame" x="20" y="1319" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="taJ-sg-wnU" id="axB-si-1KM">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
@@ -523,7 +523,7 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="zMz-hU-UYU" style="IBUITableViewCellStyleDefault" id="OXi-cg-ab9" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="1365.5" width="374" height="43.5"/>
<rect key="frame" x="20" y="1362.5" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="OXi-cg-ab9" id="npR-a0-9wv">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
@@ -540,7 +540,7 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="T7x-zl-6Yf" style="IBUITableViewCellStyleDefault" id="VpI-0o-3Px" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="1409" width="374" height="43.5"/>
<rect key="frame" x="20" y="1406" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VpI-0o-3Px" id="xRH-i4-vne">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
@@ -557,7 +557,7 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="76A-Ng-kfs" style="IBUITableViewCellStyleDefault" id="jK8-tv-hBD" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="1452.5" width="374" height="43.5"/>
<rect key="frame" x="20" y="1449.5" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="jK8-tv-hBD" id="I7Q-GQ-u8Y">
<rect key="frame" x="0.0" y="0.0" width="355.5" height="43.5"/>
@@ -1079,10 +1079,10 @@
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="NotificationsCell" id="guK-h7-1U7" customClass="NotificationsTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="55.5" width="374" height="43.5"/>
<rect key="frame" x="20" y="55.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="guK-h7-1U7" id="7Rd-Ur-EwD">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ggq-dZ-hv7">
@@ -1093,7 +1093,7 @@
<color key="onTintColor" name="AccentColor"/>
</switch>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="78k-PU-gKA">
<rect key="frame" x="58" y="11" width="241" height="21"/>
<rect key="frame" x="58" y="11" width="241" height="21.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@@ -1124,7 +1124,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="OpenSettingsCell" textLabel="5OE-bg-gWy" style="IBUITableViewCellStyleDefault" id="LXf-Fb-We7" customClass="VibrantBasicTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="99" width="374" height="43.5"/>
<rect key="frame" x="20" y="99.5" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="LXf-Fb-We7" id="k2T-id-bci">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>

View File

@@ -0,0 +1,24 @@
//
// SafariView.swift
// NetNewsWire-iOS
//
// Created by Stuart Breckenridge on 12/11/2022.
// Copyright © 2022 Ranchero Software. All rights reserved.
//
import SwiftUI
import SafariServices
struct SafariView: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context: UIViewControllerRepresentableContext<SafariView>) -> SFSafariViewController {
return SFSafariViewController(url: url)
}
func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext<SafariView>) {
}
}